MDL-29415 xmldb - fix get_columns() in oracle to get char based lengths + test
[moodle.git] / lib / dml / simpletest / testdml.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * @package    core
20  * @subpackage dml
21  * @copyright  2008 Petr Skoda (http://skodak.org)
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 defined('MOODLE_INTERNAL') || die();
27 class dml_test extends UnitTestCase {
28     private $tables = array();
29     /** @var moodle_database */
30     private $tdb;
31     private $data;
32     public  static $includecoverage = array('lib/dml');
33     public  static $excludecoverage = array('lib/dml/simpletest');
35     protected $olddebug;
36     protected $olddisplay;
38     function setUp() {
39         global $DB, $UNITTEST;
41         if (isset($UNITTEST->func_test_db)) {
42             $this->tdb = $UNITTEST->func_test_db;
43         } else {
44             $this->tdb = $DB;
45         }
46     }
48     function tearDown() {
49         $dbman = $this->tdb->get_manager();
51         foreach ($this->tables as $tablename) {
52             if ($dbman->table_exists($tablename)) {
53                 $table = new xmldb_table($tablename);
54                 $dbman->drop_table($table);
55             }
56         }
57         $this->tables = array();
58     }
60     /**
61      * Get a xmldb_table object for testing, deleting any existing table
62      * of the same name, for example if one was left over from a previous test
63      * run that crashed.
64      *
65      * @param database_manager $dbman the database_manager to use.
66      * @param string $suffix table name suffix, use if you need more test tables
67      * @return xmldb_table the table object.
68      */
69     private function get_test_table($suffix = '') {
70         $dbman = $this->tdb->get_manager();
72         $tablename = "unit_table";
73         if ($suffix !== '') {
74             $tablename .= $suffix;
75         }
77         $table = new xmldb_table($tablename);
78         if ($dbman->table_exists($table)) {
79             $dbman->drop_table($table);
80         }
81         $table->setComment("This is a test'n drop table. You can drop it safely");
82         $this->tables[$tablename] = $tablename;
83         return new xmldb_table($tablename);
84     }
86     protected function enable_debugging() {
87         global $CFG;
89         $this->olddebug   = $CFG->debug;       // Save current debug settings
90         $this->olddisplay = $CFG->debugdisplay;
91         $CFG->debug = DEBUG_DEVELOPER;
92         $CFG->debugdisplay = true;
93         ob_start(); // hide debug warning
95     }
97     protected function get_debugging() {
98         global $CFG;
100         $debuginfo = ob_get_contents();
101         ob_end_clean();
102         $CFG->debug = $this->olddebug;         // Restore original debug settings
103         $CFG->debugdisplay = $this->olddisplay;
105         return $debuginfo;
106     }
108     // NOTE: please keep order of test methods here matching the order of moodle_database class methods
110     function test_diagnose() {
111         $DB = $this->tdb;
112         $result = $DB->diagnose();
113         $this->assertNull($result, 'Database self diagnostics failed %s');
114     }
116     function test_get_server_info() {
117         $DB = $this->tdb;
118         $result = $DB->get_server_info();
119         $this->assertTrue(is_array($result));
120         $this->assertTrue(array_key_exists('description', $result));
121         $this->assertTrue(array_key_exists('version', $result));
122     }
124     public function test_get_in_or_equal() {
125         $DB = $this->tdb;
127         // SQL_PARAMS_QM - IN or =
129         // Correct usage of multiple values
130         $in_values = array('value1', 'value2', '3', 4, null, false, true);
131         list($usql, $params) = $DB->get_in_or_equal($in_values);
132         $this->assertEqual('IN ('.implode(',',array_fill(0, count($in_values), '?')).')', $usql);
133         $this->assertEqual(count($in_values), count($params));
134         foreach ($params as $key => $value) {
135             $this->assertIdentical($in_values[$key], $value);
136         }
138         // Correct usage of single value (in an array)
139         $in_values = array('value1');
140         list($usql, $params) = $DB->get_in_or_equal($in_values);
141         $this->assertEqual("= ?", $usql);
142         $this->assertEqual(1, count($params));
143         $this->assertEqual($in_values[0], $params[0]);
145         // Correct usage of single value
146         $in_value = 'value1';
147         list($usql, $params) = $DB->get_in_or_equal($in_values);
148         $this->assertEqual("= ?", $usql);
149         $this->assertEqual(1, count($params));
150         $this->assertEqual($in_value, $params[0]);
152         // SQL_PARAMS_QM - NOT IN or <>
154         // Correct usage of multiple values
155         $in_values = array('value1', 'value2', 'value3', 'value4');
156         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
157         $this->assertEqual("NOT IN (?,?,?,?)", $usql);
158         $this->assertEqual(4, count($params));
159         foreach ($params as $key => $value) {
160             $this->assertEqual($in_values[$key], $value);
161         }
163         // Correct usage of single value (in array()
164         $in_values = array('value1');
165         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
166         $this->assertEqual("<> ?", $usql);
167         $this->assertEqual(1, count($params));
168         $this->assertEqual($in_values[0], $params[0]);
170         // Correct usage of single value
171         $in_value = 'value1';
172         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
173         $this->assertEqual("<> ?", $usql);
174         $this->assertEqual(1, count($params));
175         $this->assertEqual($in_value, $params[0]);
177         // SQL_PARAMS_NAMED - IN or =
179         // Correct usage of multiple values
180         $in_values = array('value1', 'value2', 'value3', 'value4');
181         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true);
182         $this->assertEqual(4, count($params));
183         reset($in_values);
184         $ps = array();
185         foreach ($params as $key => $value) {
186             $this->assertEqual(current($in_values), $value);
187             next($in_values);
188             $ps[] = ':'.$key;
189         }
190         $this->assertEqual("IN (".implode(',', $ps).")", $usql);
192         // Correct usage of single values (in array)
193         $in_values = array('value1');
194         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true);
195         $this->assertEqual(1, count($params));
196         $value = reset($params);
197         $key = key($params);
198         $this->assertEqual("= :$key", $usql);
199         $this->assertEqual($in_value, $value);
201         // Correct usage of single value
202         $in_value = 'value1';
203         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true);
204         $this->assertEqual(1, count($params));
205         $value = reset($params);
206         $key = key($params);
207         $this->assertEqual("= :$key", $usql);
208         $this->assertEqual($in_value, $value);
210         // SQL_PARAMS_NAMED - NOT IN or <>
212         // Correct usage of multiple values
213         $in_values = array('value1', 'value2', 'value3', 'value4');
214         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
215         $this->assertEqual(4, count($params));
216         reset($in_values);
217         $ps = array();
218         foreach ($params as $key => $value) {
219             $this->assertEqual(current($in_values), $value);
220             next($in_values);
221             $ps[] = ':'.$key;
222         }
223         $this->assertEqual("NOT IN (".implode(',', $ps).")", $usql);
225         // Correct usage of single values (in array)
226         $in_values = array('value1');
227         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
228         $this->assertEqual(1, count($params));
229         $value = reset($params);
230         $key = key($params);
231         $this->assertEqual("<> :$key", $usql);
232         $this->assertEqual($in_value, $value);
234         // Correct usage of single value
235         $in_value = 'value1';
236         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
237         $this->assertEqual(1, count($params));
238         $value = reset($params);
239         $key = key($params);
240         $this->assertEqual("<> :$key", $usql);
241         $this->assertEqual($in_value, $value);
243         // make sure the param names are unique
244         list($usql1, $params1) = $DB->get_in_or_equal(array(1,2,3), SQL_PARAMS_NAMED, 'param');
245         list($usql2, $params2) = $DB->get_in_or_equal(array(1,2,3), SQL_PARAMS_NAMED, 'param');
246         $params1 = array_keys($params1);
247         $params2 = array_keys($params2);
248         $common = array_intersect($params1, $params2);
249         $this->assertEqual(count($common), 0);
251         // Some incorrect tests
253         // Incorrect usage passing not-allowed params type
254         $in_values = array(1, 2, 3);
255         try {
256             list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_DOLLAR, 'param', false);
257             $this->fail('An Exception is missing, expected due to not supported SQL_PARAMS_DOLLAR');
258         } catch (exception $e) {
259             $this->assertTrue($e instanceof dml_exception);
260             $this->assertEqual($e->errorcode, 'typenotimplement');
261         }
263         // Incorrect usage passing empty array
264         $in_values = array();
265         try {
266             list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
267             $this->fail('An Exception is missing, expected due to empty array of items');
268         } catch (exception $e) {
269             $this->assertTrue($e instanceof coding_exception);
270         }
272         // Test using $onemptyitems
274         // Correct usage passing empty array and $onemptyitems = NULL (equal = true, QM)
275         $in_values = array();
276         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, NULL);
277         $this->assertEqual(' IS NULL', $usql);
278         $this->assertIdentical(array(), $params);
280         // Correct usage passing empty array and $onemptyitems = NULL (equal = false, NAMED)
281         $in_values = array();
282         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, NULL);
283         $this->assertEqual(' IS NOT NULL', $usql);
284         $this->assertIdentical(array(), $params);
286         // Correct usage passing empty array and $onemptyitems = true (equal = true, QM)
287         $in_values = array();
288         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, true);
289         $this->assertEqual('= ?', $usql);
290         $this->assertIdentical(array(true), $params);
292         // Correct usage passing empty array and $onemptyitems = true (equal = false, NAMED)
293         $in_values = array();
294         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, true);
295         $this->assertEqual(1, count($params));
296         $value = reset($params);
297         $key = key($params);
298         $this->assertEqual('<> :'.$key, $usql);
299         $this->assertIdentical($value, true);
301         // Correct usage passing empty array and $onemptyitems = -1 (equal = true, QM)
302         $in_values = array();
303         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, -1);
304         $this->assertEqual('= ?', $usql);
305         $this->assertIdentical(array(-1), $params);
307         // Correct usage passing empty array and $onemptyitems = -1 (equal = false, NAMED)
308         $in_values = array();
309         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, -1);
310         $this->assertEqual(1, count($params));
311         $value = reset($params);
312         $key = key($params);
313         $this->assertEqual('<> :'.$key, $usql);
314         $this->assertIdentical($value, -1);
316         // Correct usage passing empty array and $onemptyitems = 'onevalue' (equal = true, QM)
317         $in_values = array();
318         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, 'onevalue');
319         $this->assertEqual('= ?', $usql);
320         $this->assertIdentical(array('onevalue'), $params);
322         // Correct usage passing empty array and $onemptyitems = 'onevalue' (equal = false, NAMED)
323         $in_values = array();
324         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, 'onevalue');
325         $this->assertEqual(1, count($params));
326         $value = reset($params);
327         $key = key($params);
328         $this->assertEqual('<> :'.$key, $usql);
329         $this->assertIdentical($value, 'onevalue');
330     }
332     public function test_fix_table_names() {
333         $DB = new moodle_database_for_testing();
334         $prefix = $DB->get_prefix();
336         // Simple placeholder
337         $placeholder = "{user_123}";
338         $this->assertIdentical($prefix."user_123", $DB->public_fix_table_names($placeholder));
340         // wrong table name
341         $placeholder = "{user-a}";
342         $this->assertIdentical($placeholder, $DB->public_fix_table_names($placeholder));
344         // wrong table name
345         $placeholder = "{123user}";
346         $this->assertIdentical($placeholder, $DB->public_fix_table_names($placeholder));
348         // Full SQL
349         $sql = "SELECT * FROM {user}, {funny_table_name}, {mdl_stupid_table} WHERE {user}.id = {funny_table_name}.userid";
350         $expected = "SELECT * FROM {$prefix}user, {$prefix}funny_table_name, {$prefix}mdl_stupid_table WHERE {$prefix}user.id = {$prefix}funny_table_name.userid";
351         $this->assertIdentical($expected, $DB->public_fix_table_names($sql));
352     }
354     function test_fix_sql_params() {
355         $DB = $this->tdb;
357         $table = $this->get_test_table();
358         $tablename = $table->getName();
360         // Correct table placeholder substitution
361         $sql = "SELECT * FROM {{$tablename}}";
362         $sqlarray = $DB->fix_sql_params($sql);
363         $this->assertEqual("SELECT * FROM {$DB->get_prefix()}".$tablename, $sqlarray[0]);
365         // Conversions of all param types
366         $sql = array();
367         $sql[SQL_PARAMS_NAMED]  = "SELECT * FROM {$DB->get_prefix()}testtable WHERE name = :param1, course = :param2";
368         $sql[SQL_PARAMS_QM]     = "SELECT * FROM {$DB->get_prefix()}testtable WHERE name = ?, course = ?";
369         $sql[SQL_PARAMS_DOLLAR] = "SELECT * FROM {$DB->get_prefix()}testtable WHERE name = \$1, course = \$2";
371         $params = array();
372         $params[SQL_PARAMS_NAMED]  = array('param1'=>'first record', 'param2'=>1);
373         $params[SQL_PARAMS_QM]     = array('first record', 1);
374         $params[SQL_PARAMS_DOLLAR] = array('first record', 1);
376         list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_NAMED], $params[SQL_PARAMS_NAMED]);
377         $this->assertIdentical($rsql, $sql[$rtype]);
378         $this->assertIdentical($rparams, $params[$rtype]);
380         list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_QM], $params[SQL_PARAMS_QM]);
381         $this->assertIdentical($rsql, $sql[$rtype]);
382         $this->assertIdentical($rparams, $params[$rtype]);
384         list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_DOLLAR], $params[SQL_PARAMS_DOLLAR]);
385         $this->assertIdentical($rsql, $sql[$rtype]);
386         $this->assertIdentical($rparams, $params[$rtype]);
389         // Malformed table placeholder
390         $sql = "SELECT * FROM [testtable]";
391         $sqlarray = $DB->fix_sql_params($sql);
392         $this->assertIdentical($sql, $sqlarray[0]);
395         // Mixed param types (colon and dollar)
396         $sql = "SELECT * FROM {{$tablename}} WHERE name = :param1, course = \$1";
397         $params = array('param1' => 'record1', 'param2' => 3);
398         try {
399             $DB->fix_sql_params($sql, $params);
400             $this->fail("Expecting an exception, none occurred");
401         } catch (Exception $e) {
402             $this->assertTrue($e instanceof dml_exception);
403         }
405         // Mixed param types (question and dollar)
406         $sql = "SELECT * FROM {{$tablename}} WHERE name = ?, course = \$1";
407         $params = array('param1' => 'record2', 'param2' => 5);
408         try {
409             $DB->fix_sql_params($sql, $params);
410             $this->fail("Expecting an exception, none occurred");
411         } catch (Exception $e) {
412             $this->assertTrue($e instanceof dml_exception);
413         }
415         // Too few params in sql
416         $sql = "SELECT * FROM {{$tablename}} WHERE name = ?, course = ?, id = ?";
417         $params = array('record2', 3);
418         try {
419             $DB->fix_sql_params($sql, $params);
420             $this->fail("Expecting an exception, none occurred");
421         } catch (Exception $e) {
422             $this->assertTrue($e instanceof dml_exception);
423         }
425         // Too many params in array: no error, just use what is necessary
426         $params[] = 1;
427         $params[] = time();
428         try {
429             $sqlarray = $DB->fix_sql_params($sql, $params);
430             $this->assertTrue(is_array($sqlarray));
431             $this->assertEqual(count($sqlarray[1]), 3);
432         } catch (Exception $e) {
433             $this->fail("Unexpected ".get_class($e)." exception");
434         }
436         // Named params missing from array
437         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :course";
438         $params = array('wrongname' => 'record1', 'course' => 1);
439         try {
440             $DB->fix_sql_params($sql, $params);
441             $this->fail("Expecting an exception, none occurred");
442         } catch (Exception $e) {
443             $this->assertTrue($e instanceof dml_exception);
444         }
446         // Duplicate named param in query - this is a very important feature!!
447         // it helps with debugging of sloppy code
448         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :name";
449         $params = array('name' => 'record2', 'course' => 3);
450         try {
451             $DB->fix_sql_params($sql, $params);
452             $this->fail("Expecting an exception, none occurred");
453         } catch (Exception $e) {
454             $this->assertTrue($e instanceof dml_exception);
455         }
457         // Extra named param is ignored
458         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :course";
459         $params = array('name' => 'record1', 'course' => 1, 'extrastuff'=>'haha');
460         try {
461             $sqlarray = $DB->fix_sql_params($sql, $params);
462             $this->assertTrue(is_array($sqlarray));
463             $this->assertEqual(count($sqlarray[1]), 2);
464         } catch (Exception $e) {
465             $this->fail("Unexpected ".get_class($e)." exception");
466         }
468         // Params exceeding 30 chars length
469         $sql = "SELECT * FROM {{$tablename}} WHERE name = :long_placeholder_with_more_than_30";
470         $params = array('long_placeholder_with_more_than_30' => 'record1');
471         try {
472             $DB->fix_sql_params($sql, $params);
473             $this->fail("Expecting an exception, none occurred");
474         } catch (Exception $e) {
475             $this->assertTrue($e instanceof coding_exception);
476         }
478         // Booleans in NAMED params are casting to 1/0 int
479         $sql = "SELECT * FROM {{$tablename}} WHERE course = ? OR course = ?";
480         $params = array(true, false);
481         list($sql, $params) = $DB->fix_sql_params($sql, $params);
482         $this->assertTrue(reset($params) === 1);
483         $this->assertTrue(next($params) === 0);
485         // Booleans in QM params are casting to 1/0 int
486         $sql = "SELECT * FROM {{$tablename}} WHERE course = :course1 OR course = :course2";
487         $params = array('course1' => true, 'course2' => false);
488         list($sql, $params) = $DB->fix_sql_params($sql, $params);
489         $this->assertTrue(reset($params) === 1);
490         $this->assertTrue(next($params) === 0);
492         // Booleans in DOLLAR params are casting to 1/0 int
493         $sql = "SELECT * FROM {{$tablename}} WHERE course = \$1 OR course = \$2";
494         $params = array(true, false);
495         list($sql, $params) = $DB->fix_sql_params($sql, $params);
496         $this->assertTrue(reset($params) === 1);
497         $this->assertTrue(next($params) === 0);
499         // No data types are touched except bool
500         $sql = "SELECT * FROM {{$tablename}} WHERE name IN (?,?,?,?,?,?)";
501         $inparams = array('abc', 'ABC', NULL, '1', 1, 1.4);
502         list($sql, $params) = $DB->fix_sql_params($sql, $inparams);
503         $this->assertIdentical(array_values($params), array_values($inparams));
504     }
506     public function test_strtok() {
507         // strtok was previously used by bound emulation, make sure it is not used any more
508         $DB = $this->tdb;
509         $dbman = $this->tdb->get_manager();
511         $table = $this->get_test_table();
512         $tablename = $table->getName();
514         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
515         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
516         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala');
517         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
518         $dbman->create_table($table);
520         $str = 'a?b?c?d';
521         $this->assertIdentical(strtok($str, '?'), 'a');
523         $DB->get_records($tablename, array('id'=>1));
525         $this->assertIdentical(strtok('?'), 'b');
526     }
528     public function test_tweak_param_names() {
529         // Note the tweak_param_names() method is only available in the oracle driver,
530         // hence we look for expected results indirectly, by testing various DML methods
531         // with some "extreme" conditions causing the tweak to happen.
532         $DB = $this->tdb;
533         $dbman = $this->tdb->get_manager();
535         $table = $this->get_test_table();
536         $tablename = $table->getName();
538         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
539         // Add some columns with 28 chars in the name
540         $table->add_field('long_int_columnname_with_28c', XMLDB_TYPE_INTEGER, '10');
541         $table->add_field('long_dec_columnname_with_28c', XMLDB_TYPE_NUMBER, '10,2');
542         $table->add_field('long_str_columnname_with_28c', XMLDB_TYPE_CHAR, '100');
543         // Add some columns with 30 chars in the name
544         $table->add_field('long_int_columnname_with_30cxx', XMLDB_TYPE_INTEGER, '10');
545         $table->add_field('long_dec_columnname_with_30cxx', XMLDB_TYPE_NUMBER, '10,2');
546         $table->add_field('long_str_columnname_with_30cxx', XMLDB_TYPE_CHAR, '100');
548         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
550         $dbman->create_table($table);
552         $this->assertTrue($dbman->table_exists($tablename));
554         // Test insert record
555         $rec1 = new stdClass();
556         $rec1->long_int_columnname_with_28c = 28;
557         $rec1->long_dec_columnname_with_28c = 28.28;
558         $rec1->long_str_columnname_with_28c = '28';
559         $rec1->long_int_columnname_with_30cxx = 30;
560         $rec1->long_dec_columnname_with_30cxx = 30.30;
561         $rec1->long_str_columnname_with_30cxx = '30';
563         // insert_record()
564         $rec1->id = $DB->insert_record($tablename, $rec1);
565         $this->assertEqual($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
567         // update_record()
568         $DB->update_record($tablename, $rec1);
569         $this->assertEqual($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
571         // set_field()
572         $rec1->long_int_columnname_with_28c = 280;
573         $DB->set_field($tablename, 'long_int_columnname_with_28c', $rec1->long_int_columnname_with_28c,
574             array('id' => $rec1->id, 'long_int_columnname_with_28c' => 28));
575         $rec1->long_dec_columnname_with_28c = 280.28;
576         $DB->set_field($tablename, 'long_dec_columnname_with_28c', $rec1->long_dec_columnname_with_28c,
577             array('id' => $rec1->id, 'long_dec_columnname_with_28c' => 28.28));
578         $rec1->long_str_columnname_with_28c = '280';
579         $DB->set_field($tablename, 'long_str_columnname_with_28c', $rec1->long_str_columnname_with_28c,
580             array('id' => $rec1->id, 'long_str_columnname_with_28c' => '28'));
581         $rec1->long_int_columnname_with_30cxx = 300;
582         $DB->set_field($tablename, 'long_int_columnname_with_30cxx', $rec1->long_int_columnname_with_30cxx,
583             array('id' => $rec1->id, 'long_int_columnname_with_30cxx' => 30));
584         $rec1->long_dec_columnname_with_30cxx = 300.30;
585         $DB->set_field($tablename, 'long_dec_columnname_with_30cxx', $rec1->long_dec_columnname_with_30cxx,
586             array('id' => $rec1->id, 'long_dec_columnname_with_30cxx' => 30.30));
587         $rec1->long_str_columnname_with_30cxx = '300';
588         $DB->set_field($tablename, 'long_str_columnname_with_30cxx', $rec1->long_str_columnname_with_30cxx,
589             array('id' => $rec1->id, 'long_str_columnname_with_30cxx' => '30'));
590         $this->assertEqual($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
592         // delete_records()
593         $rec2 = $DB->get_record($tablename, array('id' => $rec1->id));
594         $rec2->id = $DB->insert_record($tablename, $rec2);
595         $this->assertEqual(2, $DB->count_records($tablename));
596         $DB->delete_records($tablename, (array) $rec2);
597         $this->assertEqual(1, $DB->count_records($tablename));
599         // get_recordset()
600         $rs = $DB->get_recordset($tablename, (array) $rec1);
601         $iterations = 0;
602         foreach ($rs as $rec2) {
603             $iterations++;
604         }
605         $rs->close();
606         $this->assertEqual(1, $iterations);
607         $this->assertEqual($rec1, $rec2);
609         // get_records()
610         $recs = $DB->get_records($tablename, (array) $rec1);
611         $this->assertEqual(1, count($recs));
612         $this->assertEqual($rec1, reset($recs));
614         // get_fieldset_select()
615         $select = 'id = :id AND
616                    long_int_columnname_with_28c = :long_int_columnname_with_28c AND
617                    long_dec_columnname_with_28c = :long_dec_columnname_with_28c AND
618                    long_str_columnname_with_28c = :long_str_columnname_with_28c AND
619                    long_int_columnname_with_30cxx = :long_int_columnname_with_30cxx AND
620                    long_dec_columnname_with_30cxx = :long_dec_columnname_with_30cxx AND
621                    long_str_columnname_with_30cxx = :long_str_columnname_with_30cxx';
622         $fields = $DB->get_fieldset_select($tablename, 'long_int_columnname_with_28c', $select, (array)$rec1);
623         $this->assertEqual(1, count($fields));
624         $this->assertEqual($rec1->long_int_columnname_with_28c, reset($fields));
625         $fields = $DB->get_fieldset_select($tablename, 'long_dec_columnname_with_28c', $select, (array)$rec1);
626         $this->assertEqual($rec1->long_dec_columnname_with_28c, reset($fields));
627         $fields = $DB->get_fieldset_select($tablename, 'long_str_columnname_with_28c', $select, (array)$rec1);
628         $this->assertEqual($rec1->long_str_columnname_with_28c, reset($fields));
629         $fields = $DB->get_fieldset_select($tablename, 'long_int_columnname_with_30cxx', $select, (array)$rec1);
630         $this->assertEqual($rec1->long_int_columnname_with_30cxx, reset($fields));
631         $fields = $DB->get_fieldset_select($tablename, 'long_dec_columnname_with_30cxx', $select, (array)$rec1);
632         $this->assertEqual($rec1->long_dec_columnname_with_30cxx, reset($fields));
633         $fields = $DB->get_fieldset_select($tablename, 'long_str_columnname_with_30cxx', $select, (array)$rec1);
634         $this->assertEqual($rec1->long_str_columnname_with_30cxx, reset($fields));
636         // overlapping placeholders (progressive str_replace)
637         $overlapselect = 'id = :p AND
638                    long_int_columnname_with_28c = :param1 AND
639                    long_dec_columnname_with_28c = :param2 AND
640                    long_str_columnname_with_28c = :param_with_29_characters_long AND
641                    long_int_columnname_with_30cxx = :param_with_30_characters_long_ AND
642                    long_dec_columnname_with_30cxx = :param_ AND
643                    long_str_columnname_with_30cxx = :param__';
644         $overlapparams = array(
645                 'p' => $rec1->id,
646                 'param1' => $rec1->long_int_columnname_with_28c,
647                 'param2' => $rec1->long_dec_columnname_with_28c,
648                 'param_with_29_characters_long' => $rec1->long_str_columnname_with_28c,
649                 'param_with_30_characters_long_' => $rec1->long_int_columnname_with_30cxx,
650                 'param_' => $rec1->long_dec_columnname_with_30cxx,
651                 'param__' => $rec1->long_str_columnname_with_30cxx);
652         $recs = $DB->get_records_select($tablename, $overlapselect, $overlapparams);
653         $this->assertEqual(1, count($recs));
654         $this->assertEqual($rec1, reset($recs));
656         // execute()
657         $DB->execute("DELETE FROM {{$tablename}} WHERE $select", (array)$rec1);
658         $this->assertEqual(0, $DB->count_records($tablename));
659     }
661     public function test_get_tables() {
662         $DB = $this->tdb;
663         $dbman = $this->tdb->get_manager();
665         // Need to test with multiple DBs
666         $table = $this->get_test_table();
667         $tablename = $table->getName();
669         $original_count = count($DB->get_tables());
671         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
672         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
674         $dbman->create_table($table);
675         $this->assertTrue(count($DB->get_tables()) == $original_count + 1);
677         $dbman->drop_table($table);
678         $this->assertTrue(count($DB->get_tables()) == $original_count);
679     }
681     public function test_get_indexes() {
682         $DB = $this->tdb;
683         $dbman = $this->tdb->get_manager();
685         $table = $this->get_test_table();
686         $tablename = $table->getName();
688         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
689         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
690         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
691         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
692         $table->add_index('course-id', XMLDB_INDEX_UNIQUE, array('course', 'id'));
693         $dbman->create_table($table);
695         $indices = $DB->get_indexes($tablename);
696         $this->assertTrue(is_array($indices));
697         $this->assertEqual(count($indices), 2);
698         // we do not care about index names for now
699         $first = array_shift($indices);
700         $second = array_shift($indices);
701         if (count($first['columns']) == 2) {
702             $composed = $first;
703             $single   = $second;
704         } else {
705             $composed = $second;
706             $single   = $first;
707         }
708         $this->assertFalse($single['unique']);
709         $this->assertTrue($composed['unique']);
710         $this->assertEqual(1, count($single['columns']));
711         $this->assertEqual(2, count($composed['columns']));
712         $this->assertEqual('course', $single['columns'][0]);
713         $this->assertEqual('course', $composed['columns'][0]);
714         $this->assertEqual('id', $composed['columns'][1]);
715     }
717     public function test_get_columns() {
718         $DB = $this->tdb;
719         $dbman = $this->tdb->get_manager();
721         $table = $this->get_test_table();
722         $tablename = $table->getName();
724         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
725         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
726         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala');
727         $table->add_field('description', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
728         $table->add_field('enumfield', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'test2');
729         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
730         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
731         $dbman->create_table($table);
733         $columns = $DB->get_columns($tablename);
734         $this->assertTrue(is_array($columns));
736         $fields = $table->getFields();
737         $this->assertEqual(count($columns), count($fields));
739         $field = $columns['id'];
740         $this->assertEqual('R', $field->meta_type);
741         $this->assertTrue($field->auto_increment);
742         $this->assertTrue($field->unique);
744         $field = $columns['course'];
745         $this->assertEqual('I', $field->meta_type);
746         $this->assertFalse($field->auto_increment);
747         $this->assertTrue($field->has_default);
748         $this->assertEqual(0, $field->default_value);
749         $this->assertTrue($field->not_null);
751         $field = $columns['name'];
752         $this->assertEqual('C', $field->meta_type);
753         $this->assertFalse($field->auto_increment);
754         $this->assertEqual(255, $field->max_length);
755         $this->assertTrue($field->has_default);
756         $this->assertIdentical('lala', $field->default_value);
757         $this->assertFalse($field->not_null);
759         $field = $columns['description'];
760         $this->assertEqual('X', $field->meta_type);
761         $this->assertFalse($field->auto_increment);
762         $this->assertFalse($field->has_default);
763         $this->assertIdentical(null, $field->default_value);
764         $this->assertFalse($field->not_null);
766         $field = $columns['enumfield'];
767         $this->assertEqual('C', $field->meta_type);
768         $this->assertFalse($field->auto_increment);
769         $this->assertIdentical('test2', $field->default_value);
770         $this->assertTrue($field->not_null);
772         $field = $columns['onenum'];
773         $this->assertEqual('N', $field->meta_type);
774         $this->assertFalse($field->auto_increment);
775         $this->assertTrue($field->has_default);
776         $this->assertEqual(200.0, $field->default_value);
777         $this->assertFalse($field->not_null);
779         for ($i = 0; $i < count($columns); $i++) {
780             if ($i == 0) {
781                 $next_column = reset($columns);
782                 $next_field  = reset($fields);
783             } else {
784                 $next_column = next($columns);
785                 $next_field  = next($fields);
786             }
788             $this->assertEqual($next_column->name, $next_field->name);
789         }
790     }
792     public function test_get_manager() {
793         $DB = $this->tdb;
794         $dbman = $this->tdb->get_manager();
796         $this->assertTrue($dbman instanceof database_manager);
797     }
799     public function test_setup_is_unicodedb() {
800         $DB = $this->tdb;
801         $this->assertTrue($DB->setup_is_unicodedb());
802     }
804     public function test_set_debug() { //tests get_debug() too
805         $DB = $this->tdb;
806         $dbman = $this->tdb->get_manager();
808         $table = $this->get_test_table();
809         $tablename = $table->getName();
811         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
812         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
813         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
814         $dbman->create_table($table);
816         $sql = "SELECT * FROM {{$tablename}}";
818         $prevdebug = $DB->get_debug();
820         ob_start();
821         $DB->set_debug(true);
822         $this->assertTrue($DB->get_debug());
823         $DB->execute($sql);
824         $DB->set_debug(false);
825         $this->assertFalse($DB->get_debug());
826         $debuginfo = ob_get_contents();
827         ob_end_clean();
828         $this->assertFalse($debuginfo === '');
830         ob_start();
831         $DB->execute($sql);
832         $debuginfo = ob_get_contents();
833         ob_end_clean();
834         $this->assertTrue($debuginfo === '');
836         $DB->set_debug($prevdebug);
837     }
839     public function test_execute() {
840         $DB = $this->tdb;
841         $dbman = $this->tdb->get_manager();
843         $table1 = $this->get_test_table('1');
844         $tablename1 = $table1->getName();
845         $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
846         $table1->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
847         $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
848         $table1->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
849         $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
850         $dbman->create_table($table1);
852         $table2 = $this->get_test_table('2');
853         $tablename2 = $table2->getName();
854         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
855         $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
856         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
857         $dbman->create_table($table2);
859         $DB->insert_record($tablename1, array('course' => 3, 'name' => 'aaa'));
860         $DB->insert_record($tablename1, array('course' => 1, 'name' => 'bbb'));
861         $DB->insert_record($tablename1, array('course' => 7, 'name' => 'ccc'));
862         $DB->insert_record($tablename1, array('course' => 3, 'name' => 'ddd'));
864         // select results are ignored
865         $sql = "SELECT * FROM {{$tablename1}} WHERE course = :course";
866         $this->assertTrue($DB->execute($sql, array('course'=>3)));
868         // throw exception on error
869         $sql = "XXUPDATE SET XSSD";
870         try {
871             $DB->execute($sql);
872             $this->fail("Expecting an exception, none occurred");
873         } catch (Exception $e) {
874             $this->assertTrue($e instanceof dml_write_exception);
875         }
877         // update records
878         $sql = "UPDATE {{$tablename1}}
879                    SET course = 6
880                  WHERE course = ?";
881         $this->assertTrue($DB->execute($sql, array('3')));
882         $this->assertEqual($DB->count_records($tablename1, array('course' => 6)), 2);
884         // insert from one into second table
885         $sql = "INSERT INTO {{$tablename2}} (course)
887                 SELECT course
888                   FROM {{$tablename1}}";
889         $this->assertTrue($DB->execute($sql));
890         $this->assertEqual($DB->count_records($tablename2), 4);
891     }
893     public function test_get_recordset() {
894         $DB = $this->tdb;
895         $dbman = $DB->get_manager();
897         $table = $this->get_test_table();
898         $tablename = $table->getName();
900         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
901         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
902         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
903         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
904         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
905         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
906         $dbman->create_table($table);
908         $data = array(array('id' => 1, 'course' => 3, 'name' => 'record1', 'onetext'=>'abc'),
909                       array('id' => 2, 'course' => 3, 'name' => 'record2', 'onetext'=>'abcd'),
910                       array('id' => 3, 'course' => 5, 'name' => 'record3', 'onetext'=>'abcde'));
912         foreach ($data as $record) {
913             $DB->insert_record($tablename, $record);
914         }
916         // standard recordset iteration
917         $rs = $DB->get_recordset($tablename);
918         $this->assertTrue($rs instanceof moodle_recordset);
919         reset($data);
920         foreach($rs as $record) {
921             $data_record = current($data);
922             foreach ($record as $k => $v) {
923                 $this->assertEqual($data_record[$k], $v);
924             }
925             next($data);
926         }
927         $rs->close();
929         // iterator style usage
930         $rs = $DB->get_recordset($tablename);
931         $this->assertTrue($rs instanceof moodle_recordset);
932         reset($data);
933         while ($rs->valid()) {
934             $record = $rs->current();
935             $data_record = current($data);
936             foreach ($record as $k => $v) {
937                 $this->assertEqual($data_record[$k], $v);
938             }
939             next($data);
940             $rs->next();
941         }
942         $rs->close();
944         // make sure rewind is ignored
945         $rs = $DB->get_recordset($tablename);
946         $this->assertTrue($rs instanceof moodle_recordset);
947         reset($data);
948         $i = 0;
949         foreach($rs as $record) {
950             $i++;
951             $rs->rewind();
952             if ($i > 10) {
953                 $this->fail('revind not ignored in recordsets');
954                 break;
955             }
956             $data_record = current($data);
957             foreach ($record as $k => $v) {
958                 $this->assertEqual($data_record[$k], $v);
959             }
960             next($data);
961         }
962         $rs->close();
964         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
965         $conditions = array('onetext' => '1');
966         try {
967             $rs = $DB->get_recordset($tablename, $conditions);
968             $this->fail('An Exception is missing, expected due to equating of text fields');
969         } catch (exception $e) {
970             $this->assertTrue($e instanceof dml_exception);
971             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
972         }
974         // notes:
975         //  * limits are tested in test_get_recordset_sql()
976         //  * where_clause() is used internally and is tested in test_get_records()
977     }
979     public function test_get_recordset_iterator_keys() {
980         $DB = $this->tdb;
981         $dbman = $DB->get_manager();
983         $table = $this->get_test_table();
984         $tablename = $table->getName();
986         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
987         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
988         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
989         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
990         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
991         $dbman->create_table($table);
993         $data = array(array('id'=> 1, 'course' => 3, 'name' => 'record1'),
994                       array('id'=> 2, 'course' => 3, 'name' => 'record2'),
995                       array('id'=> 3, 'course' => 5, 'name' => 'record3'));
996         foreach ($data as $record) {
997             $DB->insert_record($tablename, $record);
998         }
1000         // Test repeated numeric keys are returned ok
1001         $rs = $DB->get_recordset($tablename, NULL, NULL, 'course, name, id');
1003         reset($data);
1004         $count = 0;
1005         foreach($rs as $key => $record) {
1006             $data_record = current($data);
1007             $this->assertEqual($data_record['course'], $key);
1008             next($data);
1009             $count++;
1010         }
1011         $rs->close();
1012         $this->assertEqual($count, 3);
1014         // Test string keys are returned ok
1015         $rs = $DB->get_recordset($tablename, NULL, NULL, 'name, course, id');
1017         reset($data);
1018         $count = 0;
1019         foreach($rs as $key => $record) {
1020             $data_record = current($data);
1021             $this->assertEqual($data_record['name'], $key);
1022             next($data);
1023             $count++;
1024         }
1025         $rs->close();
1026         $this->assertEqual($count, 3);
1028         // Test numeric not starting in 1 keys are returned ok
1029         $rs = $DB->get_recordset($tablename, NULL, 'id DESC', 'id, course, name');
1031         $data = array_reverse($data);
1032         reset($data);
1033         $count = 0;
1034         foreach($rs as $key => $record) {
1035             $data_record = current($data);
1036             $this->assertEqual($data_record['id'], $key);
1037             next($data);
1038             $count++;
1039         }
1040         $rs->close();
1041         $this->assertEqual($count, 3);
1042     }
1044     public function test_get_recordset_list() {
1045         $DB = $this->tdb;
1046         $dbman = $DB->get_manager();
1048         $table = $this->get_test_table();
1049         $tablename = $table->getName();
1051         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1052         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1053         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
1054         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1055         $dbman->create_table($table);
1057         $DB->insert_record($tablename, array('course' => 3));
1058         $DB->insert_record($tablename, array('course' => 3));
1059         $DB->insert_record($tablename, array('course' => 5));
1060         $DB->insert_record($tablename, array('course' => 2));
1062         $rs = $DB->get_recordset_list($tablename, 'course', array(3, 2));
1064         $counter = 0;
1065         foreach ($rs as $record) {
1066             $counter++;
1067         }
1068         $this->assertEqual(3, $counter);
1069         $rs->close();
1071         $rs = $DB->get_recordset_list($tablename, 'course',array()); /// Must return 0 rows without conditions. MDL-17645
1073         $counter = 0;
1074         foreach ($rs as $record) {
1075             $counter++;
1076         }
1077         $rs->close();
1078         $this->assertEqual(0, $counter);
1080         // notes:
1081         //  * limits are tested in test_get_recordset_sql()
1082         //  * where_clause() is used internally and is tested in test_get_records()
1083     }
1085     public function test_get_recordset_select() {
1086         $DB = $this->tdb;
1087         $dbman = $DB->get_manager();
1089         $table = $this->get_test_table();
1090         $tablename = $table->getName();
1092         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1093         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1094         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1095         $dbman->create_table($table);
1097         $DB->insert_record($tablename, array('course' => 3));
1098         $DB->insert_record($tablename, array('course' => 3));
1099         $DB->insert_record($tablename, array('course' => 5));
1100         $DB->insert_record($tablename, array('course' => 2));
1102         $rs = $DB->get_recordset_select($tablename, '');
1103         $counter = 0;
1104         foreach ($rs as $record) {
1105             $counter++;
1106         }
1107         $rs->close();
1108         $this->assertEqual(4, $counter);
1110         $this->assertTrue($rs = $DB->get_recordset_select($tablename, 'course = 3'));
1111         $counter = 0;
1112         foreach ($rs as $record) {
1113             $counter++;
1114         }
1115         $rs->close();
1116         $this->assertEqual(2, $counter);
1118         // notes:
1119         //  * limits are tested in test_get_recordset_sql()
1120     }
1122     public function test_get_recordset_sql() {
1123         $DB = $this->tdb;
1124         $dbman = $DB->get_manager();
1126         $table = $this->get_test_table();
1127         $tablename = $table->getName();
1129         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1130         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1131         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1132         $dbman->create_table($table);
1134         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
1135         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
1136         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
1137         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
1138         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
1139         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
1140         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
1142         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
1143         $counter = 0;
1144         foreach ($rs as $record) {
1145             $counter++;
1146         }
1147         $rs->close();
1148         $this->assertEqual(2, $counter);
1150         // limits - only need to test this case, the rest have been tested by test_get_records_sql()
1151         // only limitfrom = skips that number of records
1152         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
1153         $records = array();
1154         foreach($rs as $key => $record) {
1155             $records[$key] = $record;
1156         }
1157         $rs->close();
1158         $this->assertEqual(5, count($records));
1159         $this->assertEqual($inskey3, reset($records)->id);
1160         $this->assertEqual($inskey7, end($records)->id);
1162         // note: fetching nulls, empties, LOBs already tested by test_insert_record() no needed here
1163     }
1165     public function test_get_records() {
1166         $DB = $this->tdb;
1167         $dbman = $DB->get_manager();
1169         $table = $this->get_test_table();
1170         $tablename = $table->getName();
1172         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1173         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1174         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1175         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1176         $dbman->create_table($table);
1178         $DB->insert_record($tablename, array('course' => 3));
1179         $DB->insert_record($tablename, array('course' => 3));
1180         $DB->insert_record($tablename, array('course' => 5));
1181         $DB->insert_record($tablename, array('course' => 2));
1183         // All records
1184         $records = $DB->get_records($tablename);
1185         $this->assertEqual(4, count($records));
1186         $this->assertEqual(3, $records[1]->course);
1187         $this->assertEqual(3, $records[2]->course);
1188         $this->assertEqual(5, $records[3]->course);
1189         $this->assertEqual(2, $records[4]->course);
1191         // Records matching certain conditions
1192         $records = $DB->get_records($tablename, array('course' => 3));
1193         $this->assertEqual(2, count($records));
1194         $this->assertEqual(3, $records[1]->course);
1195         $this->assertEqual(3, $records[2]->course);
1197         // All records sorted by course
1198         $records = $DB->get_records($tablename, null, 'course');
1199         $this->assertEqual(4, count($records));
1200         $current_record = reset($records);
1201         $this->assertEqual(4, $current_record->id);
1202         $current_record = next($records);
1203         $this->assertEqual(1, $current_record->id);
1204         $current_record = next($records);
1205         $this->assertEqual(2, $current_record->id);
1206         $current_record = next($records);
1207         $this->assertEqual(3, $current_record->id);
1209         // All records, but get only one field
1210         $records = $DB->get_records($tablename, null, '', 'id');
1211         $this->assertFalse(isset($records[1]->course));
1212         $this->assertTrue(isset($records[1]->id));
1213         $this->assertEqual(4, count($records));
1215         // Booleans into params
1216         $records = $DB->get_records($tablename, array('course' => true));
1217         $this->assertEqual(0, count($records));
1218         $records = $DB->get_records($tablename, array('course' => false));
1219         $this->assertEqual(0, count($records));
1221         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
1222         $conditions = array('onetext' => '1');
1223         try {
1224             $records = $DB->get_records($tablename, $conditions);
1225             $this->fail('An Exception is missing, expected due to equating of text fields');
1226         } catch (exception $e) {
1227             $this->assertTrue($e instanceof dml_exception);
1228             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
1229         }
1231         // note: delegate limits testing to test_get_records_sql()
1232     }
1234     public function test_get_records_list() {
1235         $DB = $this->tdb;
1236         $dbman = $DB->get_manager();
1238         $table = $this->get_test_table();
1239         $tablename = $table->getName();
1241         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1242         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1243         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1244         $dbman->create_table($table);
1246         $DB->insert_record($tablename, array('course' => 3));
1247         $DB->insert_record($tablename, array('course' => 3));
1248         $DB->insert_record($tablename, array('course' => 5));
1249         $DB->insert_record($tablename, array('course' => 2));
1251         $records = $DB->get_records_list($tablename, 'course', array(3, 2));
1252         $this->assertTrue(is_array($records));
1253         $this->assertEqual(3, count($records));
1254         $this->assertEqual(1, reset($records)->id);
1255         $this->assertEqual(2, next($records)->id);
1256         $this->assertEqual(4, next($records)->id);
1258         $this->assertIdentical(array(), $records = $DB->get_records_list($tablename, 'course', array())); /// Must return 0 rows without conditions. MDL-17645
1259         $this->assertEqual(0, count($records));
1261         // note: delegate limits testing to test_get_records_sql()
1262     }
1264     public function test_get_records_sql() {
1265         $DB = $this->tdb;
1266         $dbman = $DB->get_manager();
1268         $table = $this->get_test_table();
1269         $tablename = $table->getName();
1271         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1272         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1273         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1274         $dbman->create_table($table);
1276         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
1277         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
1278         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
1279         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
1280         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
1281         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
1282         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
1284         $table2 = $this->get_test_table("2");
1285         $tablename2 = $table2->getName();
1286         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1287         $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1288         $table2->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
1289         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1290         $dbman->create_table($table2);
1292         $DB->insert_record($tablename2, array('course'=>3, 'nametext'=>'badabing'));
1293         $DB->insert_record($tablename2, array('course'=>4, 'nametext'=>'badabang'));
1294         $DB->insert_record($tablename2, array('course'=>5, 'nametext'=>'badabung'));
1295         $DB->insert_record($tablename2, array('course'=>6, 'nametext'=>'badabong'));
1297         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
1298         $this->assertEqual(2, count($records));
1299         $this->assertEqual($inskey1, reset($records)->id);
1300         $this->assertEqual($inskey4, next($records)->id);
1302         // Awful test, requires debug enabled and sent to browser. Let's do that and restore after test
1303         $this->enable_debugging();
1304         $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null);
1305         $this->assertFalse($this->get_debugging() === '');
1306         $this->assertEqual(6, count($records));
1308         // negative limits = no limits
1309         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, -1, -1);
1310         $this->assertEqual(7, count($records));
1312         // zero limits = no limits
1313         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 0);
1314         $this->assertEqual(7, count($records));
1316         // only limitfrom = skips that number of records
1317         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
1318         $this->assertEqual(5, count($records));
1319         $this->assertEqual($inskey3, reset($records)->id);
1320         $this->assertEqual($inskey7, end($records)->id);
1322         // only limitnum = fetches that number of records
1323         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 3);
1324         $this->assertEqual(3, count($records));
1325         $this->assertEqual($inskey1, reset($records)->id);
1326         $this->assertEqual($inskey3, end($records)->id);
1328         // both limitfrom and limitnum = skips limitfrom records and fetches limitnum ones
1329         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 3, 2);
1330         $this->assertEqual(2, count($records));
1331         $this->assertEqual($inskey4, reset($records)->id);
1332         $this->assertEqual($inskey5, end($records)->id);
1334         // both limitfrom and limitnum in query having subqueris
1335         // note the subquery skips records with course = 0 and 3
1336         $sql = "SELECT * FROM {{$tablename}}
1337                  WHERE course NOT IN (
1338                      SELECT course FROM {{$tablename}}
1339                       WHERE course IN (0, 3))
1340                 ORDER BY course";
1341         $records = $DB->get_records_sql($sql, null, 0, 2); // Skip 0, get 2
1342         $this->assertEqual(2, count($records));
1343         $this->assertEqual($inskey6, reset($records)->id);
1344         $this->assertEqual($inskey5, end($records)->id);
1345         $records = $DB->get_records_sql($sql, null, 2, 2); // Skip 2, get 2
1346         $this->assertEqual(2, count($records));
1347         $this->assertEqual($inskey3, reset($records)->id);
1348         $this->assertEqual($inskey2, end($records)->id);
1350         // test 2 tables with aliases and limits with order bys
1351         $sql = "SELECT t1.id, t1.course AS cid, t2.nametext
1352                   FROM {{$tablename}} t1, {{$tablename2}} t2
1353                  WHERE t2.course=t1.course
1354               ORDER BY t1.course, ". $DB->sql_compare_text('t2.nametext');
1355         $records = $DB->get_records_sql($sql, null, 2, 2); // Skip courses 3 and 6, get 4 and 5
1356         $this->assertEqual(2, count($records));
1357         $this->assertEqual('5', end($records)->cid);
1358         $this->assertEqual('4', reset($records)->cid);
1360         // test 2 tables with aliases and limits with the highest INT limit works
1361         $records = $DB->get_records_sql($sql, null, 2, PHP_INT_MAX); // Skip course {3,6}, get {4,5}
1362         $this->assertEqual(2, count($records));
1363         $this->assertEqual('5', end($records)->cid);
1364         $this->assertEqual('4', reset($records)->cid);
1366         // test 2 tables with aliases and limits with order bys (limit which is highest INT number)
1367         $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, 2); // Skip all courses
1368         $this->assertEqual(0, count($records));
1370         // test 2 tables with aliases and limits with order bys (limit which s highest INT number)
1371         $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, PHP_INT_MAX); // Skip all courses
1372         $this->assertEqual(0, count($records));
1374         // TODO: Test limits in queries having DISTINCT clauses
1376         // note: fetching nulls, empties, LOBs already tested by test_update_record() no needed here
1377     }
1379     public function test_get_records_menu() {
1380         $DB = $this->tdb;
1381         $dbman = $DB->get_manager();
1383         $table = $this->get_test_table();
1384         $tablename = $table->getName();
1386         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1387         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1388         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1389         $dbman->create_table($table);
1391         $DB->insert_record($tablename, array('course' => 3));
1392         $DB->insert_record($tablename, array('course' => 3));
1393         $DB->insert_record($tablename, array('course' => 5));
1394         $DB->insert_record($tablename, array('course' => 2));
1396         $records = $DB->get_records_menu($tablename, array('course' => 3));
1397         $this->assertTrue(is_array($records));
1398         $this->assertEqual(2, count($records));
1399         $this->assertFalse(empty($records[1]));
1400         $this->assertFalse(empty($records[2]));
1401         $this->assertEqual(3, $records[1]);
1402         $this->assertEqual(3, $records[2]);
1404         // note: delegate limits testing to test_get_records_sql()
1405     }
1407     public function test_get_records_select_menu() {
1408         $DB = $this->tdb;
1409         $dbman = $DB->get_manager();
1411         $table = $this->get_test_table();
1412         $tablename = $table->getName();
1414         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1415         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1416         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1417         $dbman->create_table($table);
1419         $DB->insert_record($tablename, array('course' => 3));
1420         $DB->insert_record($tablename, array('course' => 2));
1421         $DB->insert_record($tablename, array('course' => 3));
1422         $DB->insert_record($tablename, array('course' => 5));
1424         $records = $DB->get_records_select_menu($tablename, "course > ?", array(2));
1425         $this->assertTrue(is_array($records));
1427         $this->assertEqual(3, count($records));
1428         $this->assertFalse(empty($records[1]));
1429         $this->assertTrue(empty($records[2]));
1430         $this->assertFalse(empty($records[3]));
1431         $this->assertFalse(empty($records[4]));
1432         $this->assertEqual(3, $records[1]);
1433         $this->assertEqual(3, $records[3]);
1434         $this->assertEqual(5, $records[4]);
1436         // note: delegate limits testing to test_get_records_sql()
1437     }
1439     public function test_get_records_sql_menu() {
1440         $DB = $this->tdb;
1441         $dbman = $DB->get_manager();
1443         $table = $this->get_test_table();
1444         $tablename = $table->getName();
1446         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1447         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1448         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1449         $dbman->create_table($table);
1451         $DB->insert_record($tablename, array('course' => 3));
1452         $DB->insert_record($tablename, array('course' => 2));
1453         $DB->insert_record($tablename, array('course' => 3));
1454         $DB->insert_record($tablename, array('course' => 5));
1456         $records = $DB->get_records_sql_menu("SELECT * FROM {{$tablename}} WHERE course > ?", array(2));
1457         $this->assertTrue(is_array($records));
1459         $this->assertEqual(3, count($records));
1460         $this->assertFalse(empty($records[1]));
1461         $this->assertTrue(empty($records[2]));
1462         $this->assertFalse(empty($records[3]));
1463         $this->assertFalse(empty($records[4]));
1464         $this->assertEqual(3, $records[1]);
1465         $this->assertEqual(3, $records[3]);
1466         $this->assertEqual(5, $records[4]);
1468         // note: delegate limits testing to test_get_records_sql()
1469     }
1471     public function test_get_record() {
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($tablename, array('id' => 2));
1487         $this->assertTrue($record instanceof stdClass);
1489         $this->assertEqual(2, $record->course);
1490         $this->assertEqual(2, $record->id);
1491     }
1494     public function test_get_record_select() {
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         $record = $DB->get_record_select($tablename, "id = ?", array(2));
1510         $this->assertTrue($record instanceof stdClass);
1512         $this->assertEqual(2, $record->course);
1514         // note: delegates limit testing to test_get_records_sql()
1515     }
1517     public function test_get_record_sql() {
1518         $DB = $this->tdb;
1519         $dbman = $DB->get_manager();
1521         $table = $this->get_test_table();
1522         $tablename = $table->getName();
1524         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1525         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1526         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1527         $dbman->create_table($table);
1529         $DB->insert_record($tablename, array('course' => 3));
1530         $DB->insert_record($tablename, array('course' => 2));
1532         // standard use
1533         $record = $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(2));
1534         $this->assertTrue($record instanceof stdClass);
1535         $this->assertEqual(2, $record->course);
1536         $this->assertEqual(2, $record->id);
1538         // backwards compatibility with $ignoremultiple
1539         $this->assertFalse(IGNORE_MISSING);
1540         $this->assertTrue(IGNORE_MULTIPLE);
1542         // record not found - ignore
1543         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MISSING));
1544         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MULTIPLE));
1546         // record not found error
1547         try {
1548             $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), MUST_EXIST);
1549             $this->fail("Exception expected");
1550         } catch (dml_missing_record_exception $e) {
1551             $this->assertTrue(true);
1552         }
1554         $this->enable_debugging();
1555         $this->assertTrue($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING));
1556         $this->assertFalse($this->get_debugging() === '');
1558         // multiple matches ignored
1559         $this->assertTrue($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MULTIPLE));
1561         // multiple found error
1562         try {
1563             $DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), MUST_EXIST);
1564             $this->fail("Exception expected");
1565         } catch (dml_multiple_records_exception $e) {
1566             $this->assertTrue(true);
1567         }
1568     }
1570     public function test_get_field() {
1571         $DB = $this->tdb;
1572         $dbman = $DB->get_manager();
1574         $table = $this->get_test_table();
1575         $tablename = $table->getName();
1577         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1578         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1579         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1580         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1581         $dbman->create_table($table);
1583         $id1 = $DB->insert_record($tablename, array('course' => 3));
1584         $DB->insert_record($tablename, array('course' => 5));
1585         $DB->insert_record($tablename, array('course' => 5));
1587         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id1)));
1588         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('course' => 3)));
1590         $this->assertIdentical(false, $DB->get_field($tablename, 'course', array('course' => 11), IGNORE_MISSING));
1591         try {
1592             $DB->get_field($tablename, 'course', array('course' => 4), MUST_EXIST);
1593             $this->assertFail('Exception expected due to missing record');
1594         } catch (dml_exception $ex) {
1595             $this->assertTrue(true);
1596         }
1598         $this->enable_debugging();
1599         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MULTIPLE));
1600         $this->assertIdentical($this->get_debugging(), '');
1602         $this->enable_debugging();
1603         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MISSING));
1604         $this->assertFalse($this->get_debugging() === '');
1606         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
1607         $conditions = array('onetext' => '1');
1608         try {
1609             $DB->get_field($tablename, 'course', $conditions);
1610             $this->fail('An Exception is missing, expected due to equating of text fields');
1611         } catch (exception $e) {
1612             $this->assertTrue($e instanceof dml_exception);
1613             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
1614         }
1615     }
1617     public function test_get_field_select() {
1618         $DB = $this->tdb;
1619         $dbman = $DB->get_manager();
1621         $table = $this->get_test_table();
1622         $tablename = $table->getName();
1624         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1625         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1626         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1627         $dbman->create_table($table);
1629         $DB->insert_record($tablename, array('course' => 3));
1631         $this->assertEqual(3, $DB->get_field_select($tablename, 'course', "id = ?", array(1)));
1632     }
1634     public function test_get_field_sql() {
1635         $DB = $this->tdb;
1636         $dbman = $DB->get_manager();
1638         $table = $this->get_test_table();
1639         $tablename = $table->getName();
1641         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1642         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1643         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1644         $dbman->create_table($table);
1646         $DB->insert_record($tablename, array('course' => 3));
1648         $this->assertEqual(3, $DB->get_field_sql("SELECT course FROM {{$tablename}} WHERE id = ?", array(1)));
1649     }
1651     public function test_get_fieldset_select() {
1652         $DB = $this->tdb;
1653         $dbman = $DB->get_manager();
1655         $table = $this->get_test_table();
1656         $tablename = $table->getName();
1658         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1659         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1660         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1661         $dbman->create_table($table);
1663         $DB->insert_record($tablename, array('course' => 1));
1664         $DB->insert_record($tablename, array('course' => 3));
1665         $DB->insert_record($tablename, array('course' => 2));
1666         $DB->insert_record($tablename, array('course' => 6));
1668         $fieldset = $DB->get_fieldset_select($tablename, 'course', "course > ?", array(1));
1669         $this->assertTrue(is_array($fieldset));
1671         $this->assertEqual(3, count($fieldset));
1672         $this->assertEqual(3, $fieldset[0]);
1673         $this->assertEqual(2, $fieldset[1]);
1674         $this->assertEqual(6, $fieldset[2]);
1675     }
1677     public function test_get_fieldset_sql() {
1678         $DB = $this->tdb;
1679         $dbman = $DB->get_manager();
1681         $table = $this->get_test_table();
1682         $tablename = $table->getName();
1684         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1685         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1686         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1687         $dbman->create_table($table);
1689         $DB->insert_record($tablename, array('course' => 1));
1690         $DB->insert_record($tablename, array('course' => 3));
1691         $DB->insert_record($tablename, array('course' => 2));
1692         $DB->insert_record($tablename, array('course' => 6));
1694         $fieldset = $DB->get_fieldset_sql("SELECT * FROM {{$tablename}} WHERE course > ?", array(1));
1695         $this->assertTrue(is_array($fieldset));
1697         $this->assertEqual(3, count($fieldset));
1698         $this->assertEqual(2, $fieldset[0]);
1699         $this->assertEqual(3, $fieldset[1]);
1700         $this->assertEqual(4, $fieldset[2]);
1701     }
1703     public function test_insert_record_raw() {
1704         $DB = $this->tdb;
1705         $dbman = $DB->get_manager();
1707         $table = $this->get_test_table();
1708         $tablename = $table->getName();
1710         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1711         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1712         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1713         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1714         $dbman->create_table($table);
1716         $record = (object)array('course' => 1, 'onechar' => 'xx');
1717         $before = clone($record);
1718         $result = $DB->insert_record_raw($tablename, $record);
1719         $this->assertIdentical(1, $result);
1720         $this->assertIdentical($record, $before);
1722         $record = $DB->get_record($tablename, array('course' => 1));
1723         $this->assertTrue($record instanceof stdClass);
1724         $this->assertIdentical('xx', $record->onechar);
1726         $result = $DB->insert_record_raw($tablename, array('course' => 2, 'onechar' => 'yy'), false);
1727         $this->assertIdentical(true, $result);
1729         // note: bulk not implemented yet
1730         $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'zz'), true, true);
1731         $record = $DB->get_record($tablename, array('course' => 3));
1732         $this->assertTrue($record instanceof stdClass);
1733         $this->assertIdentical('zz', $record->onechar);
1735         // custom sequence (id) - returnid is ignored
1736         $result = $DB->insert_record_raw($tablename, array('id' => 10, 'course' => 3, 'onechar' => 'bb'), true, false, true);
1737         $this->assertIdentical(true, $result);
1738         $record = $DB->get_record($tablename, array('id' => 10));
1739         $this->assertTrue($record instanceof stdClass);
1740         $this->assertIdentical('bb', $record->onechar);
1742         // custom sequence - missing id error
1743         try {
1744             $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'bb'), true, false, true);
1745             $this->assertFail('Exception expected due to missing record');
1746         } catch (coding_exception $ex) {
1747             $this->assertTrue(true);
1748         }
1750         // wrong column error
1751         try {
1752             $DB->insert_record_raw($tablename, array('xxxxx' => 3, 'onechar' => 'bb'));
1753             $this->assertFail('Exception expected due to invalid column');
1754         } catch (dml_write_exception $ex) {
1755             $this->assertTrue(true);
1756         }
1757     }
1759     public function test_insert_record() {
1760         // All the information in this test is fetched from DB by get_recordset() so we
1761         // have such method properly tested against nulls, empties and friends...
1763         $DB = $this->tdb;
1764         $dbman = $DB->get_manager();
1766         $table = $this->get_test_table();
1767         $tablename = $table->getName();
1769         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1770         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1771         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
1772         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
1773         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1774         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1775         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
1776         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1777         $dbman->create_table($table);
1779         $this->assertIdentical(1, $DB->insert_record($tablename, array('course' => 1), true));
1780         $record = $DB->get_record($tablename, array('course' => 1));
1781         $this->assertEqual(1, $record->id);
1782         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
1783         $this->assertEqual(200, $record->onenum);
1784         $this->assertIdentical('onestring', $record->onechar);
1785         $this->assertNull($record->onetext);
1786         $this->assertNull($record->onebinary);
1788         // without returning id, bulk not implemented
1789         $result = $this->assertIdentical(true, $DB->insert_record($tablename, array('course' => 99), false, true));
1790         $record = $DB->get_record($tablename, array('course' => 99));
1791         $this->assertEqual(2, $record->id);
1792         $this->assertEqual(99, $record->course);
1794         // Check nulls are set properly for all types
1795         $record = new stdClass();
1796         $record->oneint = null;
1797         $record->onenum = null;
1798         $record->onechar = null;
1799         $record->onetext = null;
1800         $record->onebinary = null;
1801         $recid = $DB->insert_record($tablename, $record);
1802         $record = $DB->get_record($tablename, array('id' => $recid));
1803         $this->assertEqual(0, $record->course);
1804         $this->assertNull($record->oneint);
1805         $this->assertNull($record->onenum);
1806         $this->assertNull($record->onechar);
1807         $this->assertNull($record->onetext);
1808         $this->assertNull($record->onebinary);
1810         // Check zeros are set properly for all types
1811         $record = new stdClass();
1812         $record->oneint = 0;
1813         $record->onenum = 0;
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);
1819         // Check booleans are set properly for all types
1820         $record = new stdClass();
1821         $record->oneint = true; // trues
1822         $record->onenum = true;
1823         $record->onechar = true;
1824         $record->onetext = true;
1825         $recid = $DB->insert_record($tablename, $record);
1826         $record = $DB->get_record($tablename, array('id' => $recid));
1827         $this->assertEqual(1, $record->oneint);
1828         $this->assertEqual(1, $record->onenum);
1829         $this->assertEqual(1, $record->onechar);
1830         $this->assertEqual(1, $record->onetext);
1832         $record = new stdClass();
1833         $record->oneint = false; // falses
1834         $record->onenum = false;
1835         $record->onechar = false;
1836         $record->onetext = false;
1837         $recid = $DB->insert_record($tablename, $record);
1838         $record = $DB->get_record($tablename, array('id' => $recid));
1839         $this->assertEqual(0, $record->oneint);
1840         $this->assertEqual(0, $record->onenum);
1841         $this->assertEqual(0, $record->onechar);
1842         $this->assertEqual(0, $record->onetext);
1844         // Check string data causes exception in numeric types
1845         $record = new stdClass();
1846         $record->oneint = 'onestring';
1847         $record->onenum = 0;
1848         try {
1849             $DB->insert_record($tablename, $record);
1850             $this->fail("Expecting an exception, none occurred");
1851         } catch (exception $e) {
1852             $this->assertTrue($e instanceof dml_exception);
1853         }
1854         $record = new stdClass();
1855         $record->oneint = 0;
1856         $record->onenum = 'onestring';
1857         try {
1858            $DB->insert_record($tablename, $record);
1859            $this->fail("Expecting an exception, none occurred");
1860         } catch (exception $e) {
1861             $this->assertTrue($e instanceof dml_exception);
1862         }
1864         // Check empty string data is stored as 0 in numeric datatypes
1865         $record = new stdClass();
1866         $record->oneint = ''; // empty string
1867         $record->onenum = 0;
1868         $recid = $DB->insert_record($tablename, $record);
1869         $record = $DB->get_record($tablename, array('id' => $recid));
1870         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
1872         $record = new stdClass();
1873         $record->oneint = 0;
1874         $record->onenum = ''; // empty string
1875         $recid = $DB->insert_record($tablename, $record);
1876         $record = $DB->get_record($tablename, array('id' => $recid));
1877         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
1879         // Check empty strings are set properly in string types
1880         $record = new stdClass();
1881         $record->oneint = 0;
1882         $record->onenum = 0;
1883         $record->onechar = '';
1884         $record->onetext = '';
1885         $recid = $DB->insert_record($tablename, $record);
1886         $record = $DB->get_record($tablename, array('id' => $recid));
1887         $this->assertTrue($record->onechar === '');
1888         $this->assertTrue($record->onetext === '');
1890         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
1891         $record = new stdClass();
1892         $record->oneint = ((210.10 + 39.92) - 150.02);
1893         $record->onenum = ((210.10 + 39.92) - 150.02);
1894         $recid = $DB->insert_record($tablename, $record);
1895         $record = $DB->get_record($tablename, array('id' => $recid));
1896         $this->assertEqual(100, $record->oneint);
1897         $this->assertEqual(100, $record->onenum);
1899         // Check various quotes/backslashes combinations in string types
1900         $teststrings = array(
1901             'backslashes and quotes alone (even): "" \'\' \\\\',
1902             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
1903             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
1904             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
1905         foreach ($teststrings as $teststring) {
1906             $record = new stdClass();
1907             $record->onechar = $teststring;
1908             $record->onetext = $teststring;
1909             $recid = $DB->insert_record($tablename, $record);
1910             $record = $DB->get_record($tablename, array('id' => $recid));
1911             $this->assertEqual($teststring, $record->onechar);
1912             $this->assertEqual($teststring, $record->onetext);
1913         }
1915         // Check LOBs in text/binary columns
1916         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
1917         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
1918         $record = new stdClass();
1919         $record->onetext = $clob;
1920         $record->onebinary = $blob;
1921         $recid = $DB->insert_record($tablename, $record);
1922         $rs = $DB->get_recordset($tablename, array('id' => $recid));
1923         $record = $rs->current();
1924         $rs->close();
1925         $this->assertEqual($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
1926         $this->assertEqual($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
1928         // And "small" LOBs too, just in case
1929         $newclob = substr($clob, 0, 500);
1930         $newblob = substr($blob, 0, 250);
1931         $record = new stdClass();
1932         $record->onetext = $newclob;
1933         $record->onebinary = $newblob;
1934         $recid = $DB->insert_record($tablename, $record);
1935         $rs = $DB->get_recordset($tablename, array('id' => $recid));
1936         $record = $rs->current();
1937         $rs->close();
1938         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
1939         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
1940         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
1942         // And "diagnostic" LOBs too, just in case
1943         $newclob = '\'"\\;/ěščřžýáíé';
1944         $newblob = '\'"\\;/ěščřžýáíé';
1945         $record = new stdClass();
1946         $record->onetext = $newclob;
1947         $record->onebinary = $newblob;
1948         $recid = $DB->insert_record($tablename, $record);
1949         $rs = $DB->get_recordset($tablename, array('id' => $recid));
1950         $record = $rs->current();
1951         $rs->close();
1952         $this->assertIdentical($newclob, $record->onetext);
1953         $this->assertIdentical($newblob, $record->onebinary);
1954         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
1956         // test data is not modified
1957         $record = new stdClass();
1958         $record->id     = -1; // has to be ignored
1959         $record->course = 3;
1960         $record->lalala = 'lalal'; // unused
1961         $before = clone($record);
1962         $DB->insert_record($tablename, $record);
1963         $this->assertEqual($record, $before);
1965         // make sure the id is always increasing and never reuses the same id
1966         $id1 = $DB->insert_record($tablename, array('course' => 3));
1967         $id2 = $DB->insert_record($tablename, array('course' => 3));
1968         $this->assertTrue($id1 < $id2);
1969         $DB->delete_records($tablename, array('id'=>$id2));
1970         $id3 = $DB->insert_record($tablename, array('course' => 3));
1971         $this->assertTrue($id2 < $id3);
1972         $DB->delete_records($tablename, array());
1973         $id4 = $DB->insert_record($tablename, array('course' => 3));
1974         $this->assertTrue($id3 < $id4);
1976         // Test saving a float in a CHAR column, and reading it back.
1977         $id = $DB->insert_record($tablename, array('onechar' => 1.0));
1978         $this->assertEqual(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
1979         $id = $DB->insert_record($tablename, array('onechar' => 1e20));
1980         $this->assertEqual(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
1981         $id = $DB->insert_record($tablename, array('onechar' => 1e-4));
1982         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
1983         $id = $DB->insert_record($tablename, array('onechar' => 1e-5));
1984         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
1985         $id = $DB->insert_record($tablename, array('onechar' => 1e-300));
1986         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
1987         $id = $DB->insert_record($tablename, array('onechar' => 1e300));
1988         $this->assertEqual(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
1990         // Test saving a float in a TEXT column, and reading it back.
1991         $id = $DB->insert_record($tablename, array('onetext' => 1.0));
1992         $this->assertEqual(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
1993         $id = $DB->insert_record($tablename, array('onetext' => 1e20));
1994         $this->assertEqual(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
1995         $id = $DB->insert_record($tablename, array('onetext' => 1e-4));
1996         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
1997         $id = $DB->insert_record($tablename, array('onetext' => 1e-5));
1998         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
1999         $id = $DB->insert_record($tablename, array('onetext' => 1e-300));
2000         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2001         $id = $DB->insert_record($tablename, array('onetext' => 1e300));
2002         $this->assertEqual(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2003     }
2005     public function test_import_record() {
2006         // All the information in this test is fetched from DB by get_recordset() so we
2007         // have such method properly tested against nulls, empties and friends...
2009         $DB = $this->tdb;
2010         $dbman = $DB->get_manager();
2012         $table = $this->get_test_table();
2013         $tablename = $table->getName();
2015         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2016         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2017         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
2018         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
2019         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
2020         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2021         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2022         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2023         $dbman->create_table($table);
2025         $this->assertIdentical(1, $DB->insert_record($tablename, array('course' => 1), true));
2026         $record = $DB->get_record($tablename, array('course' => 1));
2027         $this->assertEqual(1, $record->id);
2028         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
2029         $this->assertEqual(200, $record->onenum);
2030         $this->assertIdentical('onestring', $record->onechar);
2031         $this->assertNull($record->onetext);
2032         $this->assertNull($record->onebinary);
2034         // ignore extra columns
2035         $record = (object)array('id'=>13, 'course'=>2, 'xxxx'=>788778);
2036         $before = clone($record);
2037         $this->assertIdentical(true, $DB->import_record($tablename, $record));
2038         $this->assertIdentical($record, $before);
2039         $records = $DB->get_records($tablename);
2040         $this->assertEqual(2, $records[13]->course);
2042         // Check nulls are set properly for all types
2043         $record = new stdClass();
2044         $record->id = 20;
2045         $record->oneint = null;
2046         $record->onenum = null;
2047         $record->onechar = null;
2048         $record->onetext = null;
2049         $record->onebinary = null;
2050         $this->assertTrue($DB->import_record($tablename, $record));
2051         $record = $DB->get_record($tablename, array('id' => 20));
2052         $this->assertEqual(0, $record->course);
2053         $this->assertNull($record->oneint);
2054         $this->assertNull($record->onenum);
2055         $this->assertNull($record->onechar);
2056         $this->assertNull($record->onetext);
2057         $this->assertNull($record->onebinary);
2059         // Check zeros are set properly for all types
2060         $record = new stdClass();
2061         $record->id = 23;
2062         $record->oneint = 0;
2063         $record->onenum = 0;
2064         $this->assertTrue($DB->import_record($tablename, $record));
2065         $record = $DB->get_record($tablename, array('id' => 23));
2066         $this->assertEqual(0, $record->oneint);
2067         $this->assertEqual(0, $record->onenum);
2069         // Check string data causes exception in numeric types
2070         $record = new stdClass();
2071         $record->id = 32;
2072         $record->oneint = 'onestring';
2073         $record->onenum = 0;
2074         try {
2075             $DB->import_record($tablename, $record);
2076             $this->fail("Expecting an exception, none occurred");
2077         } catch (exception $e) {
2078             $this->assertTrue($e instanceof dml_exception);
2079         }
2080         $record = new stdClass();
2081         $record->id = 35;
2082         $record->oneint = 0;
2083         $record->onenum = 'onestring';
2084         try {
2085            $DB->import_record($tablename, $record);
2086            $this->fail("Expecting an exception, none occurred");
2087         } catch (exception $e) {
2088             $this->assertTrue($e instanceof dml_exception);
2089         }
2091         // Check empty strings are set properly in string types
2092         $record = new stdClass();
2093         $record->id = 44;
2094         $record->oneint = 0;
2095         $record->onenum = 0;
2096         $record->onechar = '';
2097         $record->onetext = '';
2098         $this->assertTrue($DB->import_record($tablename, $record));
2099         $record = $DB->get_record($tablename, array('id' => 44));
2100         $this->assertTrue($record->onechar === '');
2101         $this->assertTrue($record->onetext === '');
2103         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2104         $record = new stdClass();
2105         $record->id = 47;
2106         $record->oneint = ((210.10 + 39.92) - 150.02);
2107         $record->onenum = ((210.10 + 39.92) - 150.02);
2108         $this->assertTrue($DB->import_record($tablename, $record));
2109         $record = $DB->get_record($tablename, array('id' => 47));
2110         $this->assertEqual(100, $record->oneint);
2111         $this->assertEqual(100, $record->onenum);
2113         // Check various quotes/backslashes combinations in string types
2114         $i = 50;
2115         $teststrings = array(
2116             'backslashes and quotes alone (even): "" \'\' \\\\',
2117             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2118             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2119             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2120         foreach ($teststrings as $teststring) {
2121             $record = new stdClass();
2122             $record->id = $i;
2123             $record->onechar = $teststring;
2124             $record->onetext = $teststring;
2125             $this->assertTrue($DB->import_record($tablename, $record));
2126             $record = $DB->get_record($tablename, array('id' => $i));
2127             $this->assertEqual($teststring, $record->onechar);
2128             $this->assertEqual($teststring, $record->onetext);
2129             $i = $i + 3;
2130         }
2132         // Check LOBs in text/binary columns
2133         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2134         $record = new stdClass();
2135         $record->id = 70;
2136         $record->onetext = $clob;
2137         $record->onebinary = '';
2138         $this->assertTrue($DB->import_record($tablename, $record));
2139         $rs = $DB->get_recordset($tablename, array('id' => 70));
2140         $record = $rs->current();
2141         $rs->close();
2142         $this->assertEqual($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
2144         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2145         $record = new stdClass();
2146         $record->id = 71;
2147         $record->onetext = '';
2148         $record->onebinary = $blob;
2149         $this->assertTrue($DB->import_record($tablename, $record));
2150         $rs = $DB->get_recordset($tablename, array('id' => 71));
2151         $record = $rs->current();
2152         $rs->close();
2153         $this->assertEqual($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
2155         // And "small" LOBs too, just in case
2156         $newclob = substr($clob, 0, 500);
2157         $newblob = substr($blob, 0, 250);
2158         $record = new stdClass();
2159         $record->id = 73;
2160         $record->onetext = $newclob;
2161         $record->onebinary = $newblob;
2162         $this->assertTrue($DB->import_record($tablename, $record));
2163         $rs = $DB->get_recordset($tablename, array('id' => 73));
2164         $record = $rs->current();
2165         $rs->close();
2166         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
2167         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
2168         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
2169     }
2171     public function test_update_record_raw() {
2172         $DB = $this->tdb;
2173         $dbman = $DB->get_manager();
2175         $table = $this->get_test_table();
2176         $tablename = $table->getName();
2178         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2179         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2180         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2181         $dbman->create_table($table);
2183         $DB->insert_record($tablename, array('course' => 1));
2184         $DB->insert_record($tablename, array('course' => 3));
2186         $record = $DB->get_record($tablename, array('course' => 1));
2187         $record->course = 2;
2188         $this->assertTrue($DB->update_record_raw($tablename, $record));
2189         $this->assertEqual(0, $DB->count_records($tablename, array('course' => 1)));
2190         $this->assertEqual(1, $DB->count_records($tablename, array('course' => 2)));
2191         $this->assertEqual(1, $DB->count_records($tablename, array('course' => 3)));
2193         $record = $DB->get_record($tablename, array('course' => 1));
2194         $record->xxxxx = 2;
2195         try {
2196            $DB->update_record_raw($tablename, $record);
2197            $this->fail("Expecting an exception, none occurred");
2198         } catch (Exception $e) {
2199             $this->assertTrue($e instanceof coding_exception);
2200         }
2202         $record = $DB->get_record($tablename, array('course' => 3));
2203         unset($record->id);
2204         try {
2205            $DB->update_record_raw($tablename, $record);
2206            $this->fail("Expecting an exception, none occurred");
2207         } catch (Exception $e) {
2208             $this->assertTrue($e instanceof coding_exception);
2209         }
2210     }
2212     public function test_update_record() {
2214         // All the information in this test is fetched from DB by get_record() so we
2215         // have such method properly tested against nulls, empties and friends...
2217         $DB = $this->tdb;
2218         $dbman = $DB->get_manager();
2220         $table = $this->get_test_table();
2221         $tablename = $table->getName();
2223         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2224         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2225         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
2226         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
2227         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
2228         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2229         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2230         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2231         $dbman->create_table($table);
2233         $DB->insert_record($tablename, array('course' => 1));
2234         $record = $DB->get_record($tablename, array('course' => 1));
2235         $record->course = 2;
2237         $this->assertTrue($DB->update_record($tablename, $record));
2238         $this->assertFalse($record = $DB->get_record($tablename, array('course' => 1)));
2239         $this->assertTrue($record = $DB->get_record($tablename, array('course' => 2)));
2240         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
2241         $this->assertEqual(200, $record->onenum);
2242         $this->assertEqual('onestring', $record->onechar);
2243         $this->assertNull($record->onetext);
2244         $this->assertNull($record->onebinary);
2246         // Check nulls are set properly for all types
2247         $record->oneint = null;
2248         $record->onenum = null;
2249         $record->onechar = null;
2250         $record->onetext = null;
2251         $record->onebinary = null;
2252         $DB->update_record($tablename, $record);
2253         $record = $DB->get_record($tablename, array('course' => 2));
2254         $this->assertNull($record->oneint);
2255         $this->assertNull($record->onenum);
2256         $this->assertNull($record->onechar);
2257         $this->assertNull($record->onetext);
2258         $this->assertNull($record->onebinary);
2260         // Check zeros are set properly for all types
2261         $record->oneint = 0;
2262         $record->onenum = 0;
2263         $DB->update_record($tablename, $record);
2264         $record = $DB->get_record($tablename, array('course' => 2));
2265         $this->assertEqual(0, $record->oneint);
2266         $this->assertEqual(0, $record->onenum);
2268         // Check booleans are set properly for all types
2269         $record->oneint = true; // trues
2270         $record->onenum = true;
2271         $record->onechar = true;
2272         $record->onetext = true;
2273         $DB->update_record($tablename, $record);
2274         $record = $DB->get_record($tablename, array('course' => 2));
2275         $this->assertEqual(1, $record->oneint);
2276         $this->assertEqual(1, $record->onenum);
2277         $this->assertEqual(1, $record->onechar);
2278         $this->assertEqual(1, $record->onetext);
2280         $record->oneint = false; // falses
2281         $record->onenum = false;
2282         $record->onechar = false;
2283         $record->onetext = false;
2284         $DB->update_record($tablename, $record);
2285         $record = $DB->get_record($tablename, array('course' => 2));
2286         $this->assertEqual(0, $record->oneint);
2287         $this->assertEqual(0, $record->onenum);
2288         $this->assertEqual(0, $record->onechar);
2289         $this->assertEqual(0, $record->onetext);
2291         // Check string data causes exception in numeric types
2292         $record->oneint = 'onestring';
2293         $record->onenum = 0;
2294         try {
2295             $DB->update_record($tablename, $record);
2296             $this->fail("Expecting an exception, none occurred");
2297         } catch (exception $e) {
2298             $this->assertTrue($e instanceof dml_exception);
2299         }
2300         $record->oneint = 0;
2301         $record->onenum = 'onestring';
2302         try {
2303             $DB->update_record($tablename, $record);
2304             $this->fail("Expecting an exception, none occurred");
2305         } catch (exception $e) {
2306             $this->assertTrue($e instanceof dml_exception);
2307         }
2309         // Check empty string data is stored as 0 in numeric datatypes
2310         $record->oneint = ''; // empty string
2311         $record->onenum = 0;
2312         $DB->update_record($tablename, $record);
2313         $record = $DB->get_record($tablename, array('course' => 2));
2314         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
2316         $record->oneint = 0;
2317         $record->onenum = ''; // empty string
2318         $DB->update_record($tablename, $record);
2319         $record = $DB->get_record($tablename, array('course' => 2));
2320         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
2322         // Check empty strings are set properly in string types
2323         $record->oneint = 0;
2324         $record->onenum = 0;
2325         $record->onechar = '';
2326         $record->onetext = '';
2327         $DB->update_record($tablename, $record);
2328         $record = $DB->get_record($tablename, array('course' => 2));
2329         $this->assertTrue($record->onechar === '');
2330         $this->assertTrue($record->onetext === '');
2332         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2333         $record->oneint = ((210.10 + 39.92) - 150.02);
2334         $record->onenum = ((210.10 + 39.92) - 150.02);
2335         $DB->update_record($tablename, $record);
2336         $record = $DB->get_record($tablename, array('course' => 2));
2337         $this->assertEqual(100, $record->oneint);
2338         $this->assertEqual(100, $record->onenum);
2340         // Check various quotes/backslashes combinations in string types
2341         $teststrings = array(
2342             'backslashes and quotes alone (even): "" \'\' \\\\',
2343             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2344             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2345             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2346         foreach ($teststrings as $teststring) {
2347             $record->onechar = $teststring;
2348             $record->onetext = $teststring;
2349             $DB->update_record($tablename, $record);
2350             $record = $DB->get_record($tablename, array('course' => 2));
2351             $this->assertEqual($teststring, $record->onechar);
2352             $this->assertEqual($teststring, $record->onetext);
2353         }
2355         // Check LOBs in text/binary columns
2356         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2357         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2358         $record->onetext = $clob;
2359         $record->onebinary = $blob;
2360         $DB->update_record($tablename, $record);
2361         $record = $DB->get_record($tablename, array('course' => 2));
2362         $this->assertEqual($clob, $record->onetext, 'Test CLOB update (full contents output disabled)');
2363         $this->assertEqual($blob, $record->onebinary, 'Test BLOB update (full contents output disabled)');
2365         // And "small" LOBs too, just in case
2366         $newclob = substr($clob, 0, 500);
2367         $newblob = substr($blob, 0, 250);
2368         $record->onetext = $newclob;
2369         $record->onebinary = $newblob;
2370         $DB->update_record($tablename, $record);
2371         $record = $DB->get_record($tablename, array('course' => 2));
2372         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB update (full contents output disabled)');
2373         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB update (full contents output disabled)');
2375         // Test saving a float in a CHAR column, and reading it back.
2376         $id = $DB->insert_record($tablename, array('onechar' => 'X'));
2377         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1.0));
2378         $this->assertEqual(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2379         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e20));
2380         $this->assertEqual(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2381         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-4));
2382         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2383         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-5));
2384         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2385         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-300));
2386         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2387         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e300));
2388         $this->assertEqual(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2390         // Test saving a float in a TEXT column, and reading it back.
2391         $id = $DB->insert_record($tablename, array('onetext' => 'X'));
2392         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1.0));
2393         $this->assertEqual(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2394         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e20));
2395         $this->assertEqual(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2396         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-4));
2397         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2398         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-5));
2399         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2400         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-300));
2401         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2402         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e300));
2403         $this->assertEqual(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2404     }
2406     public function test_set_field() {
2407         $DB = $this->tdb;
2408         $dbman = $DB->get_manager();
2410         $table = $this->get_test_table();
2411         $tablename = $table->getName();
2413         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2414         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2415         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2416         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2417         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2418         $dbman->create_table($table);
2420         // simple set_field
2421         $id1 = $DB->insert_record($tablename, array('course' => 1));
2422         $id2 = $DB->insert_record($tablename, array('course' => 1));
2423         $id3 = $DB->insert_record($tablename, array('course' => 3));
2424         $this->assertTrue($DB->set_field($tablename, 'course', 2, array('id' => $id1)));
2425         $this->assertEqual(2, $DB->get_field($tablename, 'course', array('id' => $id1)));
2426         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2427         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2428         $DB->delete_records($tablename, array());
2430         // multiple fields affected
2431         $id1 = $DB->insert_record($tablename, array('course' => 1));
2432         $id2 = $DB->insert_record($tablename, array('course' => 1));
2433         $id3 = $DB->insert_record($tablename, array('course' => 3));
2434         $DB->set_field($tablename, 'course', '5', array('course' => 1));
2435         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2436         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2437         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2438         $DB->delete_records($tablename, array());
2440         // no field affected
2441         $id1 = $DB->insert_record($tablename, array('course' => 1));
2442         $id2 = $DB->insert_record($tablename, array('course' => 1));
2443         $id3 = $DB->insert_record($tablename, array('course' => 3));
2444         $DB->set_field($tablename, 'course', '5', array('course' => 0));
2445         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id1)));
2446         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2447         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2448         $DB->delete_records($tablename, array());
2450         // all fields - no condition
2451         $id1 = $DB->insert_record($tablename, array('course' => 1));
2452         $id2 = $DB->insert_record($tablename, array('course' => 1));
2453         $id3 = $DB->insert_record($tablename, array('course' => 3));
2454         $DB->set_field($tablename, 'course', 5, array());
2455         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2456         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2457         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id3)));
2459         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2460         $conditions = array('onetext' => '1');
2461         try {
2462             $DB->set_field($tablename, 'onechar', 'frog', $conditions);
2463             $this->fail('An Exception is missing, expected due to equating of text fields');
2464         } catch (exception $e) {
2465             $this->assertTrue($e instanceof dml_exception);
2466             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2467         }
2469         // Test saving a float in a CHAR column, and reading it back.
2470         $id = $DB->insert_record($tablename, array('onechar' => 'X'));
2471         $DB->set_field($tablename, 'onechar', 1.0, array('id' => $id));
2472         $this->assertEqual(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2473         $DB->set_field($tablename, 'onechar', 1e20, array('id' => $id));
2474         $this->assertEqual(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2475         $DB->set_field($tablename, 'onechar', 1e-4, array('id' => $id));
2476         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2477         $DB->set_field($tablename, 'onechar', 1e-5, array('id' => $id));
2478         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2479         $DB->set_field($tablename, 'onechar', 1e-300, array('id' => $id));
2480         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2481         $DB->set_field($tablename, 'onechar', 1e300, array('id' => $id));
2482         $this->assertEqual(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2484         // Test saving a float in a TEXT column, and reading it back.
2485         $id = $DB->insert_record($tablename, array('onetext' => 'X'));
2486         $DB->set_field($tablename, 'onetext', 1.0, array('id' => $id));
2487         $this->assertEqual(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2488         $DB->set_field($tablename, 'onetext', 1e20, array('id' => $id));
2489         $this->assertEqual(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2490         $DB->set_field($tablename, 'onetext', 1e-4, array('id' => $id));
2491         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2492         $DB->set_field($tablename, 'onetext', 1e-5, array('id' => $id));
2493         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2494         $DB->set_field($tablename, 'onetext', 1e-300, array('id' => $id));
2495         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2496         $DB->set_field($tablename, 'onetext', 1e300, array('id' => $id));
2497         $this->assertEqual(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2499         // Note: All the nulls, booleans, empties, quoted and backslashes tests
2500         // go to set_field_select() because set_field() is just one wrapper over it
2501     }
2503     public function test_set_field_select() {
2505         // All the information in this test is fetched from DB by get_field() so we
2506         // have such method properly tested against nulls, empties and friends...
2508         $DB = $this->tdb;
2509         $dbman = $DB->get_manager();
2511         $table = $this->get_test_table();
2512         $tablename = $table->getName();
2514         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2515         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2516         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null);
2517         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null);
2518         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2519         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2520         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2521         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2522         $dbman->create_table($table);
2524         $DB->insert_record($tablename, array('course' => 1));
2526         $this->assertTrue($DB->set_field_select($tablename, 'course', 2, 'id = ?', array(1)));
2527         $this->assertEqual(2, $DB->get_field($tablename, 'course', array('id' => 1)));
2529         // Check nulls are set properly for all types
2530         $DB->set_field_select($tablename, 'oneint', null, 'id = ?', array(1)); // trues
2531         $DB->set_field_select($tablename, 'onenum', null, 'id = ?', array(1));
2532         $DB->set_field_select($tablename, 'onechar', null, 'id = ?', array(1));
2533         $DB->set_field_select($tablename, 'onetext', null, 'id = ?', array(1));
2534         $DB->set_field_select($tablename, 'onebinary', null, 'id = ?', array(1));
2535         $this->assertNull($DB->get_field($tablename, 'oneint', array('id' => 1)));
2536         $this->assertNull($DB->get_field($tablename, 'onenum', array('id' => 1)));
2537         $this->assertNull($DB->get_field($tablename, 'onechar', array('id' => 1)));
2538         $this->assertNull($DB->get_field($tablename, 'onetext', array('id' => 1)));
2539         $this->assertNull($DB->get_field($tablename, 'onebinary', array('id' => 1)));
2541         // Check zeros are set properly for all types
2542         $DB->set_field_select($tablename, 'oneint', 0, 'id = ?', array(1));
2543         $DB->set_field_select($tablename, 'onenum', 0, 'id = ?', array(1));
2544         $this->assertEqual(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2545         $this->assertEqual(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2547         // Check booleans are set properly for all types
2548         $DB->set_field_select($tablename, 'oneint', true, 'id = ?', array(1)); // trues
2549         $DB->set_field_select($tablename, 'onenum', true, 'id = ?', array(1));
2550         $DB->set_field_select($tablename, 'onechar', true, 'id = ?', array(1));
2551         $DB->set_field_select($tablename, 'onetext', true, 'id = ?', array(1));
2552         $this->assertEqual(1, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2553         $this->assertEqual(1, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2554         $this->assertEqual(1, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2555         $this->assertEqual(1, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2557         $DB->set_field_select($tablename, 'oneint', false, 'id = ?', array(1)); // falses
2558         $DB->set_field_select($tablename, 'onenum', false, 'id = ?', array(1));
2559         $DB->set_field_select($tablename, 'onechar', false, 'id = ?', array(1));
2560         $DB->set_field_select($tablename, 'onetext', false, 'id = ?', array(1));
2561         $this->assertEqual(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2562         $this->assertEqual(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2563         $this->assertEqual(0, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2564         $this->assertEqual(0, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2566         // Check string data causes exception in numeric types
2567         try {
2568             $DB->set_field_select($tablename, 'oneint', 'onestring', 'id = ?', array(1));
2569             $this->fail("Expecting an exception, none occurred");
2570         } catch (exception $e) {
2571             $this->assertTrue($e instanceof dml_exception);
2572         }
2573         try {
2574             $DB->set_field_select($tablename, 'onenum', 'onestring', 'id = ?', array(1));
2575             $this->fail("Expecting an exception, none occurred");
2576         } catch (exception $e) {
2577             $this->assertTrue($e instanceof dml_exception);
2578         }
2580         // Check empty string data is stored as 0 in numeric datatypes
2581         $DB->set_field_select($tablename, 'oneint', '', 'id = ?', array(1));
2582         $field = $DB->get_field($tablename, 'oneint', array('id' => 1));
2583         $this->assertTrue(is_numeric($field) && $field == 0);
2585         $DB->set_field_select($tablename, 'onenum', '', 'id = ?', array(1));
2586         $field = $DB->get_field($tablename, 'onenum', array('id' => 1));
2587         $this->assertTrue(is_numeric($field) && $field == 0);
2589         // Check empty strings are set properly in string types
2590         $DB->set_field_select($tablename, 'onechar', '', 'id = ?', array(1));
2591         $DB->set_field_select($tablename, 'onetext', '', 'id = ?', array(1));
2592         $this->assertTrue($DB->get_field($tablename, 'onechar', array('id' => 1)) === '');
2593         $this->assertTrue($DB->get_field($tablename, 'onetext', array('id' => 1)) === '');
2595         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2596         $DB->set_field_select($tablename, 'oneint', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
2597         $DB->set_field_select($tablename, 'onenum', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
2598         $this->assertEqual(100, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2599         $this->assertEqual(100, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2601         // Check various quotes/backslashes combinations in string types
2602         $teststrings = array(
2603             'backslashes and quotes alone (even): "" \'\' \\\\',
2604             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2605             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2606             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2607         foreach ($teststrings as $teststring) {
2608             $DB->set_field_select($tablename, 'onechar', $teststring, 'id = ?', array(1));
2609             $DB->set_field_select($tablename, 'onetext', $teststring, 'id = ?', array(1));
2610             $this->assertEqual($teststring, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2611             $this->assertEqual($teststring, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2612         }
2614         // Check LOBs in text/binary columns
2615         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2616         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2617         $DB->set_field_select($tablename, 'onetext', $clob, 'id = ?', array(1));
2618         $DB->set_field_select($tablename, 'onebinary', $blob, 'id = ?', array(1));
2619         $this->assertEqual($clob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test CLOB set_field (full contents output disabled)');
2620         $this->assertEqual($blob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test BLOB set_field (full contents output disabled)');
2622         // And "small" LOBs too, just in case
2623         $newclob = substr($clob, 0, 500);
2624         $newblob = substr($blob, 0, 250);
2625         $DB->set_field_select($tablename, 'onetext', $newclob, 'id = ?', array(1));
2626         $DB->set_field_select($tablename, 'onebinary', $newblob, 'id = ?', array(1));
2627         $this->assertEqual($newclob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test "small" CLOB set_field (full contents output disabled)');
2628         $this->assertEqual($newblob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test "small" BLOB set_field (full contents output disabled)');
2630         // This is the failure from MDL-24863. This was giving an error on MSSQL,
2631         // which converts the '1' to an integer, which cannot then be compared with
2632         // onetext cast to a varchar. This should be fixed and working now.
2633         $newchar = 'frog';
2634         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2635         $params = array('onetext' => '1');
2636         try {
2637             $DB->set_field_select($tablename, 'onechar', $newchar, $DB->sql_compare_text('onetext') . ' = ?', $params);
2638             $this->assertTrue(true, 'No exceptions thrown with numerical text param comparison for text field.');
2639         } catch (dml_exception $e) {
2640             $this->assertFalse(true, 'We have an unexpected exception.');
2641             throw $e;
2642         }
2645     }
2647     public function test_count_records() {
2648         $DB = $this->tdb;
2650         $dbman = $DB->get_manager();
2652         $table = $this->get_test_table();
2653         $tablename = $table->getName();
2655         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2656         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2657         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2658         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2659         $dbman->create_table($table);
2661         $this->assertEqual(0, $DB->count_records($tablename));
2663         $DB->insert_record($tablename, array('course' => 3));
2664         $DB->insert_record($tablename, array('course' => 4));
2665         $DB->insert_record($tablename, array('course' => 5));
2667         $this->assertEqual(3, $DB->count_records($tablename));
2669         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2670         $conditions = array('onetext' => '1');
2671         try {
2672             $DB->count_records($tablename, $conditions);
2673             $this->fail('An Exception is missing, expected due to equating of text fields');
2674         } catch (exception $e) {
2675             $this->assertTrue($e instanceof dml_exception);
2676             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2677         }
2678     }
2680     public function test_count_records_select() {
2681         $DB = $this->tdb;
2683         $dbman = $DB->get_manager();
2685         $table = $this->get_test_table();
2686         $tablename = $table->getName();
2688         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2689         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2690         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2691         $dbman->create_table($table);
2693         $this->assertEqual(0, $DB->count_records($tablename));
2695         $DB->insert_record($tablename, array('course' => 3));
2696         $DB->insert_record($tablename, array('course' => 4));
2697         $DB->insert_record($tablename, array('course' => 5));
2699         $this->assertEqual(2, $DB->count_records_select($tablename, 'course > ?', array(3)));
2700     }
2702     public function test_count_records_sql() {
2703         $DB = $this->tdb;
2704         $dbman = $DB->get_manager();
2706         $table = $this->get_test_table();
2707         $tablename = $table->getName();
2709         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2710         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2711         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2712         $dbman->create_table($table);
2714         $this->assertEqual(0, $DB->count_records($tablename));
2716         $DB->insert_record($tablename, array('course' => 3));
2717         $DB->insert_record($tablename, array('course' => 4));
2718         $DB->insert_record($tablename, array('course' => 5));
2720         $this->assertEqual(2, $DB->count_records_sql("SELECT COUNT(*) FROM {{$tablename}} WHERE course > ?", array(3)));
2721     }
2723     public function test_record_exists() {
2724         $DB = $this->tdb;
2725         $dbman = $DB->get_manager();
2727         $table = $this->get_test_table();
2728         $tablename = $table->getName();
2730         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2731         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2732         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2733         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2734         $dbman->create_table($table);
2736         $this->assertEqual(0, $DB->count_records($tablename));
2738         $this->assertFalse($DB->record_exists($tablename, array('course' => 3)));
2739         $DB->insert_record($tablename, array('course' => 3));
2741         $this->assertTrue($DB->record_exists($tablename, array('course' => 3)));
2744         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2745         $conditions = array('onetext' => '1');
2746         try {
2747             $DB->record_exists($tablename, $conditions);
2748             $this->fail('An Exception is missing, expected due to equating of text fields');
2749         } catch (exception $e) {
2750             $this->assertTrue($e instanceof dml_exception);
2751             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2752         }
2753     }
2755     public function test_record_exists_select() {
2756         $DB = $this->tdb;
2757         $dbman = $DB->get_manager();
2759         $table = $this->get_test_table();
2760         $tablename = $table->getName();
2762         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2763         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2764         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2765         $dbman->create_table($table);
2767         $this->assertEqual(0, $DB->count_records($tablename));
2769         $this->assertFalse($DB->record_exists_select($tablename, "course = ?", array(3)));
2770         $DB->insert_record($tablename, array('course' => 3));
2772         $this->assertTrue($DB->record_exists_select($tablename, "course = ?", array(3)));
2773     }
2775     public function test_record_exists_sql() {
2776         $DB = $this->tdb;
2777         $dbman = $DB->get_manager();
2779         $table = $this->get_test_table();
2780         $tablename = $table->getName();
2782         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2783         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2784         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2785         $dbman->create_table($table);
2787         $this->assertEqual(0, $DB->count_records($tablename));
2789         $this->assertFalse($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
2790         $DB->insert_record($tablename, array('course' => 3));
2792         $this->assertTrue($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
2793     }
2795     public function test_recordset_locks_delete() {
2796         $DB = $this->tdb;
2797         $dbman = $DB->get_manager();
2799         //Setup
2800         $table = $this->get_test_table();
2801         $tablename = $table->getName();
2803         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2804         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2805         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2806         $dbman->create_table($table);
2808         $DB->insert_record($tablename, array('course' => 1));
2809         $DB->insert_record($tablename, array('course' => 2));
2810         $DB->insert_record($tablename, array('course' => 3));
2811         $DB->insert_record($tablename, array('course' => 4));
2812         $DB->insert_record($tablename, array('course' => 5));
2813         $DB->insert_record($tablename, array('course' => 6));
2815         // Test against db write locking while on an open recordset
2816         $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // get courses = {3,4}
2817         foreach ($rs as $record) {
2818             $cid = $record->course;
2819             $DB->delete_records($tablename, array('course' => $cid));
2820             $this->assertFalse($DB->record_exists($tablename, array('course' => $cid)));
2821         }
2822         $rs->close();
2824         $this->assertEqual(4, $DB->count_records($tablename, array()));
2825     }
2827     public function test_recordset_locks_update() {
2828         $DB = $this->tdb;
2829         $dbman = $DB->get_manager();
2831         //Setup
2832         $table = $this->get_test_table();
2833         $tablename = $table->getName();
2835         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2836         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2837         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2838         $dbman->create_table($table);
2840         $DB->insert_record($tablename, array('course' => 1));
2841         $DB->insert_record($tablename, array('course' => 2));
2842         $DB->insert_record($tablename, array('course' => 3));
2843         $DB->insert_record($tablename, array('course' => 4));
2844         $DB->insert_record($tablename, array('course' => 5));
2845         $DB->insert_record($tablename, array('course' => 6));
2847         // Test against db write locking while on an open recordset
2848         $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // get courses = {3,4}
2849         foreach ($rs as $record) {
2850             $cid = $record->course;
2851             $DB->set_field($tablename, 'course', 10, array('course' => $cid));
2852             $this->assertFalse($DB->record_exists($tablename, array('course' => $cid)));
2853         }
2854         $rs->close();
2856         $this->assertEqual(2, $DB->count_records($tablename, array('course' => 10)));
2857     }
2859     public function test_delete_records() {
2860         $DB = $this->tdb;
2861         $dbman = $DB->get_manager();
2863         $table = $this->get_test_table();
2864         $tablename = $table->getName();
2866         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2867         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2868         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2869         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2870         $dbman->create_table($table);
2872         $DB->insert_record($tablename, array('course' => 3));
2873         $DB->insert_record($tablename, array('course' => 2));
2874         $DB->insert_record($tablename, array('course' => 2));
2876         // Delete all records
2877         $this->assertTrue($DB->delete_records($tablename));
2878         $this->assertEqual(0, $DB->count_records($tablename));
2880         // Delete subset of records
2881         $DB->insert_record($tablename, array('course' => 3));
2882         $DB->insert_record($tablename, array('course' => 2));
2883         $DB->insert_record($tablename, array('course' => 2));
2885         $this->assertTrue($DB->delete_records($tablename, array('course' => 2)));
2886         $this->assertEqual(1, $DB->count_records($tablename));
2888         // delete all
2889         $this->assertTrue($DB->delete_records($tablename, array()));
2890         $this->assertEqual(0, $DB->count_records($tablename));
2892         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2893         $conditions = array('onetext'=>'1');
2894         try {
2895             $DB->delete_records($tablename, $conditions);
2896             $this->fail('An Exception is missing, expected due to equating of text fields');
2897         } catch (exception $e) {
2898             $this->assertTrue($e instanceof dml_exception);
2899             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2900         }
2902         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2903         $conditions = array('onetext' => 1);
2904         try {
2905             $DB->delete_records($tablename, $conditions);
2906             $this->fail('An Exception is missing, expected due to equating of text fields');
2907         } catch (exception $e) {
2908             $this->assertTrue($e instanceof dml_exception);
2909             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2910         }
2911     }
2913     public function test_delete_records_select() {
2914         $DB = $this->tdb;
2915         $dbman = $DB->get_manager();
2917         $table = $this->get_test_table();
2918         $tablename = $table->getName();
2920         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2921         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2922         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2923         $dbman->create_table($table);
2925         $DB->insert_record($tablename, array('course' => 3));
2926         $DB->insert_record($tablename, array('course' => 2));
2927         $DB->insert_record($tablename, array('course' => 2));
2929         $this->assertTrue($DB->delete_records_select($tablename, 'course = ?', array(2)));
2930         $this->assertEqual(1, $DB->count_records($tablename));
2931     }
2933     public function test_delete_records_list() {
2934         $DB = $this->tdb;
2935         $dbman = $DB->get_manager();
2937         $table = $this->get_test_table();
2938         $tablename = $table->getName();
2940         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2941         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2942         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2943         $dbman->create_table($table);
2945         $DB->insert_record($tablename, array('course' => 1));
2946         $DB->insert_record($tablename, array('course' => 2));
2947         $DB->insert_record($tablename, array('course' => 3));
2949         $this->assertTrue($DB->delete_records_list($tablename, 'course', array(2, 3)));
2950         $this->assertEqual(1, $DB->count_records($tablename));
2952         $this->assertTrue($DB->delete_records_list($tablename, 'course', array())); /// Must delete 0 rows without conditions. MDL-17645
2953         $this->assertEqual(1, $DB->count_records($tablename));
2954     }
2956     function test_sql_null_from_clause() {
2957         $DB = $this->tdb;
2958         $sql = "SELECT 1 AS id ".$DB->sql_null_from_clause();
2959         $this->assertEqual($DB->get_field_sql($sql), 1);
2960     }
2962     function test_sql_bitand() {
2963         $DB = $this->tdb;
2964         $dbman = $DB->get_manager();
2966         $table = $this->get_test_table();
2967         $tablename = $table->getName();
2969         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2970         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2971         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2972         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2973         $dbman->create_table($table);
2975         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
2977         $sql = "SELECT ".$DB->sql_bitand(10, 3)." AS res ".$DB->sql_null_from_clause();
2978         $this->assertEqual($DB->get_field_sql($sql), 2);
2980         $sql = "SELECT id, ".$DB->sql_bitand('col1', 'col2')." AS res FROM {{$tablename}}";
2981         $result = $DB->get_records_sql($sql);
2982         $this->assertEqual(count($result), 1);
2983         $this->assertEqual(reset($result)->res, 2);
2985         $sql = "SELECT id, ".$DB->sql_bitand('col1', '?')." AS res FROM {{$tablename}}";
2986         $result = $DB->get_records_sql($sql, array(10));
2987         $this->assertEqual(count($result), 1);
2988         $this->assertEqual(reset($result)->res, 2);
2989     }
2991     function test_sql_bitnot() {
2992         $DB = $this->tdb;
2994         $not = $DB->sql_bitnot(2);
2995         $notlimited = $DB->sql_bitand($not, 7); // might be positive or negative number which can not fit into PHP INT!
2997         $sql = "SELECT $notlimited AS res ".$DB->sql_null_from_clause();
2998         $this->assertEqual($DB->get_field_sql($sql), 5);
2999     }
3001     function test_sql_bitor() {
3002         $DB = $this->tdb;
3003         $dbman = $DB->get_manager();
3005         $table = $this->get_test_table();
3006         $tablename = $table->getName();
3008         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3009         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3010         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3011         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3012         $dbman->create_table($table);
3014         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
3016         $sql = "SELECT ".$DB->sql_bitor(10, 3)." AS res ".$DB->sql_null_from_clause();
3017         $this->assertEqual($DB->get_field_sql($sql), 11);
3019         $sql = "SELECT id, ".$DB->sql_bitor('col1', 'col2')." AS res FROM {{$tablename}}";
3020         $result = $DB->get_records_sql($sql);
3021         $this->assertEqual(count($result), 1);
3022         $this->assertEqual(reset($result)->res, 11);
3024         $sql = "SELECT id, ".$DB->sql_bitor('col1', '?')." AS res FROM {{$tablename}}";
3025         $result = $DB->get_records_sql($sql, array(10));
3026         $this->assertEqual(count($result), 1);
3027         $this->assertEqual(reset($result)->res, 11);
3028     }
3030     function test_sql_bitxor() {
3031         $DB = $this->tdb;
3032         $dbman = $DB->get_manager();
3034         $table = $this->get_test_table();
3035         $tablename = $table->getName();
3037         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3038         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3039         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3040         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3041         $dbman->create_table($table);
3043         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
3045         $sql = "SELECT ".$DB->sql_bitxor(10, 3)." AS res ".$DB->sql_null_from_clause();
3046         $this->assertEqual($DB->get_field_sql($sql), 9);
3048         $sql = "SELECT id, ".$DB->sql_bitxor('col1', 'col2')." AS res FROM {{$tablename}}";
3049         $result = $DB->get_records_sql($sql);
3050         $this->assertEqual(count($result), 1);
3051         $this->assertEqual(reset($result)->res, 9);
3053         $sql = "SELECT id, ".$DB->sql_bitxor('col1', '?')." AS res FROM {{$tablename}}";
3054         $result = $DB->get_records_sql($sql, array(10));
3055         $this->assertEqual(count($result), 1);
3056         $this->assertEqual(reset($result)->res, 9);
3057     }
3059     function test_sql_modulo() {
3060         $DB = $this->tdb;
3061         $sql = "SELECT ".$DB->sql_modulo(10, 7)." AS res ".$DB->sql_null_from_clause();
3062         $this->assertEqual($DB->get_field_sql($sql), 3);
3063     }
3065     function test_sql_ceil() {
3066         $DB = $this->tdb;
3067         $sql = "SELECT ".$DB->sql_ceil(665.666)." AS res ".$DB->sql_null_from_clause();
3068         $this->assertEqual($DB->get_field_sql($sql), 666);
3069     }
3071     function test_cast_char2int() {
3072         $DB = $this->tdb;
3073         $dbman = $DB->get_manager();
3075         $table1 = $this->get_test_table("1");
3076         $tablename1 = $table1->getName();
3078         $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3079         $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3080         $table1->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
3081         $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3082         $dbman->create_table($table1);
3084         $DB->insert_record($tablename1, array('name'=>'0100', 'nametext'=>'0200'));
3085         $DB->insert_record($tablename1, array('name'=>'10',   'nametext'=>'20'));
3087         $table2 = $this->get_test_table("2");
3088         $tablename2 = $table2->getName();
3089         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3090         $table2->add_field('res', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3091         $table2->add_field('restext', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3092         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3093         $dbman->create_table($table2);
3095         $DB->insert_record($tablename2, array('res'=>100, 'restext'=>200));
3097         // casting varchar field
3098         $sql = "SELECT *
3099                   FROM {".$tablename1."} t1
3100                   JOIN {".$tablename2."} t2 ON ".$DB->sql_cast_char2int("t1.name")." = t2.res ";
3101         $records = $DB->get_records_sql($sql);
3102         $this->assertEqual(count($records), 1);
3103         // also test them in order clauses
3104         $sql = "SELECT * FROM {{$tablename1}} ORDER BY ".$DB->sql_cast_char2int('name');
3105         $records = $DB->get_records_sql($sql);
3106         $this->assertEqual(count($records), 2);
3107         $this->assertEqual(reset($records)->name, '10');
3108         $this->assertEqual(next($records)->name, '0100');
3110         // casting text field
3111         $sql = "SELECT *
3112                   FROM {".$tablename1."} t1
3113                   JOIN {".$tablename2."} t2 ON ".$DB->sql_cast_char2int("t1.nametext", true)." = t2.restext ";
3114         $records = $DB->get_records_sql($sql);
3115         $this->assertEqual(count($records), 1);
3116         // also test them in order clauses
3117         $sql = "SELECT * FROM {{$tablename1}} ORDER BY ".$DB->sql_cast_char2int('nametext', true);
3118         $records = $DB->get_records_sql($sql);
3119         $this->assertEqual(count($records), 2);
3120         $this->assertEqual(reset($records)->nametext, '20');
3121         $this->assertEqual(next($records)->nametext, '0200');
3122     }
3124     function test_cast_char2real() {
3125         $DB = $this->tdb;
3126         $dbman = $DB->get_manager();
3128         $table = $this->get_test_table();
3129         $tablename = $table->getName();
3131         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3132         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3133         $table->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
3134         $table->add_field('res', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null);
3135         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3136         $dbman->create_table($table);
3138         $DB->insert_record($tablename, array('name'=>'10.10', 'nametext'=>'10.10', 'res'=>5.1));
3139         $DB->insert_record($tablename, array('name'=>'91.10', 'nametext'=>'91.10', 'res'=>666));
3140         $DB->insert_record($tablename, array('name'=>'011.10','nametext'=>'011.10','res'=>10.1));
3142         // casting varchar field
3143         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_cast_char2real('name')." > res";
3144         $records = $DB->get_records_sql($sql);
3145         $this->assertEqual(count($records), 2);
3146         // also test them in order clauses
3147         $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_cast_char2real('name');
3148         $records = $DB->get_records_sql($sql);
3149         $this->assertEqual(count($records), 3);
3150         $this->assertEqual(reset($records)->name, '10.10');
3151         $this->assertEqual(next($records)->name, '011.10');
3152         $this->assertEqual(next($records)->name, '91.10');
3154         // casting text field
3155         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_cast_char2real('nametext', true)." > res";
3156         $records = $DB->get_records_sql($sql);
3157         $this->assertEqual(count($records), 2);
3158         // also test them in order clauses
3159         $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_cast_char2real('nametext', true);
3160         $records = $DB->get_records_sql($sql);
3161         $this->assertEqual(count($records), 3);
3162         $this->assertEqual(reset($records)->nametext, '10.10');
3163         $this->assertEqual(next($records)->nametext, '011.10');
3164         $this->assertEqual(next($records)->nametext, '91.10');
3165     }
3167     function sql_compare_text() {
3168         $DB = $this->tdb;
3169         $dbman = $DB->get_manager();
3171         $table = $this->get_test_table();
3172         $tablename = $table->getName();
3174         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3175         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3176         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
3177         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3178         $dbman->create_table($table);
3180         $DB->insert_record($tablename, array('name'=>'abcd',   'description'=>'abcd'));
3181         $DB->insert_record($tablename, array('name'=>'abcdef', 'description'=>'bbcdef'));
3182         $DB->insert_record($tablename, array('name'=>'aaaabb', 'description'=>'aaaacccccccccccccccccc'));
3184         $sql = "SELECT * FROM {{$tablename}} WHERE name = ".$DB->sql_compare_text('description');
3185         $records = $DB->get_records_sql($sql);
3186         $this->assertEqual(count($records), 1);
3188         $sql = "SELECT * FROM {{$tablename}} WHERE name = ".$DB->sql_compare_text('description', 4);
3189         $records = $DB->get_records_sql($sql);
3190         $this->assertEqual(count($records), 2);
3191     }
3193     function test_unique_index_collation_trouble() {
3194         // note: this is a work in progress, we should probably move this to ddl test
3196         $DB = $this->tdb;
3197         $dbman = $DB->get_manager();
3199         $table = $this->get_test_table();
3200         $tablename = $table->getName();
3202         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3203         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3204         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3205         $table->add_index('name', XMLDB_INDEX_UNIQUE, array('name'));
3206         $dbman->create_table($table);
3208         $DB->insert_record($tablename, array('name'=>'aaa'));
3210         try {
3211             $DB->insert_record($tablename, array('name'=>'AAA'));
3212         } catch (Exception $e) {
3213             //TODO: ignore case insensitive uniqueness problems for now
3214             //$this->fail("Unique index is case sensitive - this may cause problems in some tables");
3215         }
3217         try {
3218             $DB->insert_record($tablename, array('name'=>'aäa'));
3219             $DB->insert_record($tablename, array('name'=>'aáa'));
3220             $this->assertTrue(true);
3221         } catch (Exception $e) {
3222             $family = $DB->get_dbfamily();
3223             if ($family === 'mysql' or $family === 'mssql') {
3224                 $this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages. This is usually caused by accent insensitive default collation.");
3225             } else {
3226                 // this should not happen, PostgreSQL and Oracle do not support accent insensitive uniqueness.
3227                 $this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages.");
3228             }
3229             throw($e);
3230         }
3231     }
3233     function test_sql_binary_equal() {
3234         $DB = $this->tdb;
3235         $dbman = $DB->get_manager();
3237         $table = $this->get_test_table();
3238         $tablename = $table->getName();
3240         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3241         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3242         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3243         $dbman->create_table($table);
3245         $DB->insert_record($tablename, array('name'=>'aaa'));
3246         $DB->insert_record($tablename, array('name'=>'aáa'));
3247         $DB->insert_record($tablename, array('name'=>'aäa'));
3248         $DB->insert_record($tablename, array('name'=>'bbb'));
3249         $DB->insert_record($tablename, array('name'=>'BBB'));
3251         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array('aaa'));
3252         $this->assertEqual(count($records), 1, 'SQL operator "=" is expected to be accent sensitive');
3254         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array('bbb'));
3255         $this->assertEqual(count($records), 1, 'SQL operator "=" is expected to be case sensitive');
3256     }
3258     function test_sql_like() {
3259         $DB = $this->tdb;
3260         $dbman = $DB->get_manager();
3262         $table = $this->get_test_table();
3263         $tablename = $table->getName();
3265         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3266         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3267         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3268         $dbman->create_table($table);