See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * DML layer tests. 19 * 20 * @package core_dml 21 * @category phpunit 22 * @copyright 2008 Nicolas Connault 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 class core_dml_testcase extends database_driver_testcase { 29 30 protected function setUp() { 31 parent::setUp(); 32 $dbman = $this->tdb->get_manager(); // Loads DDL libs. 33 } 34 35 /** 36 * Get a xmldb_table object for testing, deleting any existing table 37 * of the same name, for example if one was left over from a previous test 38 * run that crashed. 39 * 40 * @param string $suffix table name suffix, use if you need more test tables 41 * @return xmldb_table the table object. 42 */ 43 private function get_test_table($suffix = '') { 44 $tablename = "test_table"; 45 if ($suffix !== '') { 46 $tablename .= $suffix; 47 } 48 49 $table = new xmldb_table($tablename); 50 $table->setComment("This is a test'n drop table. You can drop it safely"); 51 return $table; 52 } 53 54 public function test_diagnose() { 55 $DB = $this->tdb; 56 $result = $DB->diagnose(); 57 $this->assertNull($result, 'Database self diagnostics failed %s'); 58 } 59 60 public function test_get_server_info() { 61 $DB = $this->tdb; 62 $result = $DB->get_server_info(); 63 $this->assertInternalType('array', $result); 64 $this->assertArrayHasKey('description', $result); 65 $this->assertArrayHasKey('version', $result); 66 } 67 68 public function test_get_in_or_equal() { 69 $DB = $this->tdb; 70 71 // SQL_PARAMS_QM - IN or =. 72 73 // Correct usage of multiple values. 74 $in_values = array('value1', 'value2', '3', 4, null, false, true); 75 list($usql, $params) = $DB->get_in_or_equal($in_values); 76 $this->assertSame('IN ('.implode(',', array_fill(0, count($in_values), '?')).')', $usql); 77 $this->assertEquals(count($in_values), count($params)); 78 foreach ($params as $key => $value) { 79 $this->assertSame($in_values[$key], $value); 80 } 81 82 // Correct usage of single value (in an array). 83 $in_values = array('value1'); 84 list($usql, $params) = $DB->get_in_or_equal($in_values); 85 $this->assertEquals("= ?", $usql); 86 $this->assertCount(1, $params); 87 $this->assertEquals($in_values[0], $params[0]); 88 89 // Correct usage of single value. 90 $in_value = 'value1'; 91 list($usql, $params) = $DB->get_in_or_equal($in_values); 92 $this->assertEquals("= ?", $usql); 93 $this->assertCount(1, $params); 94 $this->assertEquals($in_value, $params[0]); 95 96 // SQL_PARAMS_QM - NOT IN or <>. 97 98 // Correct usage of multiple values. 99 $in_values = array('value1', 'value2', 'value3', 'value4'); 100 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false); 101 $this->assertEquals("NOT IN (?,?,?,?)", $usql); 102 $this->assertCount(4, $params); 103 foreach ($params as $key => $value) { 104 $this->assertEquals($in_values[$key], $value); 105 } 106 107 // Correct usage of single value (in array(). 108 $in_values = array('value1'); 109 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false); 110 $this->assertEquals("<> ?", $usql); 111 $this->assertCount(1, $params); 112 $this->assertEquals($in_values[0], $params[0]); 113 114 // Correct usage of single value. 115 $in_value = 'value1'; 116 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false); 117 $this->assertEquals("<> ?", $usql); 118 $this->assertCount(1, $params); 119 $this->assertEquals($in_value, $params[0]); 120 121 // SQL_PARAMS_NAMED - IN or =. 122 123 // Correct usage of multiple values. 124 $in_values = array('value1', 'value2', 'value3', 'value4'); 125 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true); 126 $this->assertCount(4, $params); 127 reset($in_values); 128 $ps = array(); 129 foreach ($params as $key => $value) { 130 $this->assertEquals(current($in_values), $value); 131 next($in_values); 132 $ps[] = ':'.$key; 133 } 134 $this->assertEquals("IN (".implode(',', $ps).")", $usql); 135 136 // Correct usage of single values (in array). 137 $in_values = array('value1'); 138 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true); 139 $this->assertCount(1, $params); 140 $value = reset($params); 141 $key = key($params); 142 $this->assertEquals("= :$key", $usql); 143 $this->assertEquals($in_value, $value); 144 145 // Correct usage of single value. 146 $in_value = 'value1'; 147 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true); 148 $this->assertCount(1, $params); 149 $value = reset($params); 150 $key = key($params); 151 $this->assertEquals("= :$key", $usql); 152 $this->assertEquals($in_value, $value); 153 154 // SQL_PARAMS_NAMED - NOT IN or <>. 155 156 // Correct usage of multiple values. 157 $in_values = array('value1', 'value2', 'value3', 'value4'); 158 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false); 159 $this->assertCount(4, $params); 160 reset($in_values); 161 $ps = array(); 162 foreach ($params as $key => $value) { 163 $this->assertEquals(current($in_values), $value); 164 next($in_values); 165 $ps[] = ':'.$key; 166 } 167 $this->assertEquals("NOT IN (".implode(',', $ps).")", $usql); 168 169 // Correct usage of single values (in array). 170 $in_values = array('value1'); 171 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false); 172 $this->assertCount(1, $params); 173 $value = reset($params); 174 $key = key($params); 175 $this->assertEquals("<> :$key", $usql); 176 $this->assertEquals($in_value, $value); 177 178 // Correct usage of single value. 179 $in_value = 'value1'; 180 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false); 181 $this->assertCount(1, $params); 182 $value = reset($params); 183 $key = key($params); 184 $this->assertEquals("<> :$key", $usql); 185 $this->assertEquals($in_value, $value); 186 187 // Make sure the param names are unique. 188 list($usql1, $params1) = $DB->get_in_or_equal(array(1, 2, 3), SQL_PARAMS_NAMED, 'param'); 189 list($usql2, $params2) = $DB->get_in_or_equal(array(1, 2, 3), SQL_PARAMS_NAMED, 'param'); 190 $params1 = array_keys($params1); 191 $params2 = array_keys($params2); 192 $common = array_intersect($params1, $params2); 193 $this->assertCount(0, $common); 194 195 // Some incorrect tests. 196 197 // Incorrect usage passing not-allowed params type. 198 $in_values = array(1, 2, 3); 199 try { 200 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_DOLLAR, 'param', false); 201 $this->fail('An Exception is missing, expected due to not supported SQL_PARAMS_DOLLAR'); 202 } catch (moodle_exception $e) { 203 $this->assertInstanceOf('dml_exception', $e); 204 $this->assertSame('typenotimplement', $e->errorcode); 205 } 206 207 // Incorrect usage passing empty array. 208 $in_values = array(); 209 try { 210 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false); 211 $this->fail('An Exception is missing, expected due to empty array of items'); 212 } catch (moodle_exception $e) { 213 $this->assertInstanceOf('coding_exception', $e); 214 } 215 216 // Test using $onemptyitems. 217 218 // Correct usage passing empty array and $onemptyitems = null (equal = true, QM). 219 $in_values = array(); 220 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, null); 221 $this->assertSame(' IS NULL', $usql); 222 $this->assertSame(array(), $params); 223 224 // Correct usage passing empty array and $onemptyitems = null (equal = false, NAMED). 225 $in_values = array(); 226 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, null); 227 $this->assertSame(' IS NOT NULL', $usql); 228 $this->assertSame(array(), $params); 229 230 // Correct usage passing empty array and $onemptyitems = true (equal = true, QM). 231 $in_values = array(); 232 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, true); 233 $this->assertSame('= ?', $usql); 234 $this->assertSame(array(true), $params); 235 236 // Correct usage passing empty array and $onemptyitems = true (equal = false, NAMED). 237 $in_values = array(); 238 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, true); 239 $this->assertCount(1, $params); 240 $value = reset($params); 241 $key = key($params); 242 $this->assertSame('<> :'.$key, $usql); 243 $this->assertSame($value, true); 244 245 // Correct usage passing empty array and $onemptyitems = -1 (equal = true, QM). 246 $in_values = array(); 247 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, -1); 248 $this->assertSame('= ?', $usql); 249 $this->assertSame(array(-1), $params); 250 251 // Correct usage passing empty array and $onemptyitems = -1 (equal = false, NAMED). 252 $in_values = array(); 253 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, -1); 254 $this->assertCount(1, $params); 255 $value = reset($params); 256 $key = key($params); 257 $this->assertSame('<> :'.$key, $usql); 258 $this->assertSame($value, -1); 259 260 // Correct usage passing empty array and $onemptyitems = 'onevalue' (equal = true, QM). 261 $in_values = array(); 262 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, 'onevalue'); 263 $this->assertSame('= ?', $usql); 264 $this->assertSame(array('onevalue'), $params); 265 266 // Correct usage passing empty array and $onemptyitems = 'onevalue' (equal = false, NAMED). 267 $in_values = array(); 268 list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, 'onevalue'); 269 $this->assertCount(1, $params); 270 $value = reset($params); 271 $key = key($params); 272 $this->assertSame('<> :'.$key, $usql); 273 $this->assertSame($value, 'onevalue'); 274 } 275 276 public function test_fix_table_names() { 277 $DB = new moodle_database_for_testing(); 278 $prefix = $DB->get_prefix(); 279 280 // Simple placeholder. 281 $placeholder = "{user_123}"; 282 $this->assertSame($prefix."user_123", $DB->public_fix_table_names($placeholder)); 283 284 // Wrong table name. 285 $placeholder = "{user-a}"; 286 $this->assertSame($placeholder, $DB->public_fix_table_names($placeholder)); 287 288 // Wrong table name. 289 $placeholder = "{123user}"; 290 $this->assertSame($placeholder, $DB->public_fix_table_names($placeholder)); 291 292 // Full SQL. 293 $sql = "SELECT * FROM {user}, {funny_table_name}, {mdl_stupid_table} WHERE {user}.id = {funny_table_name}.userid"; 294 $expected = "SELECT * FROM {$prefix}user, {$prefix}funny_table_name, {$prefix}mdl_stupid_table WHERE {$prefix}user.id = {$prefix}funny_table_name.userid"; 295 $this->assertSame($expected, $DB->public_fix_table_names($sql)); 296 } 297 298 public function test_fix_sql_params() { 299 $DB = $this->tdb; 300 $prefix = $DB->get_prefix(); 301 302 $table = $this->get_test_table(); 303 $tablename = $table->getName(); 304 305 // Correct table placeholder substitution. 306 $sql = "SELECT * FROM {{$tablename}}"; 307 $sqlarray = $DB->fix_sql_params($sql); 308 $this->assertEquals("SELECT * FROM {$prefix}".$tablename, $sqlarray[0]); 309 310 // Conversions of all param types. 311 $sql = array(); 312 $sql[SQL_PARAMS_NAMED] = "SELECT * FROM {$prefix}testtable WHERE name = :param1, course = :param2"; 313 $sql[SQL_PARAMS_QM] = "SELECT * FROM {$prefix}testtable WHERE name = ?, course = ?"; 314 $sql[SQL_PARAMS_DOLLAR] = "SELECT * FROM {$prefix}testtable WHERE name = \$1, course = \$2"; 315 316 $params = array(); 317 $params[SQL_PARAMS_NAMED] = array('param1'=>'first record', 'param2'=>1); 318 $params[SQL_PARAMS_QM] = array('first record', 1); 319 $params[SQL_PARAMS_DOLLAR] = array('first record', 1); 320 321 list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_NAMED], $params[SQL_PARAMS_NAMED]); 322 $this->assertSame($rsql, $sql[$rtype]); 323 $this->assertSame($rparams, $params[$rtype]); 324 325 list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_QM], $params[SQL_PARAMS_QM]); 326 $this->assertSame($rsql, $sql[$rtype]); 327 $this->assertSame($rparams, $params[$rtype]); 328 329 list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_DOLLAR], $params[SQL_PARAMS_DOLLAR]); 330 $this->assertSame($rsql, $sql[$rtype]); 331 $this->assertSame($rparams, $params[$rtype]); 332 333 // Malformed table placeholder. 334 $sql = "SELECT * FROM [testtable]"; 335 $sqlarray = $DB->fix_sql_params($sql); 336 $this->assertSame($sql, $sqlarray[0]); 337 338 // Mixed param types (colon and dollar). 339 $sql = "SELECT * FROM {{$tablename}} WHERE name = :param1, course = \$1"; 340 $params = array('param1' => 'record1', 'param2' => 3); 341 try { 342 $DB->fix_sql_params($sql, $params); 343 $this->fail("Expecting an exception, none occurred"); 344 } catch (moodle_exception $e) { 345 $this->assertInstanceOf('dml_exception', $e); 346 } 347 348 // Mixed param types (question and dollar). 349 $sql = "SELECT * FROM {{$tablename}} WHERE name = ?, course = \$1"; 350 $params = array('param1' => 'record2', 'param2' => 5); 351 try { 352 $DB->fix_sql_params($sql, $params); 353 $this->fail("Expecting an exception, none occurred"); 354 } catch (moodle_exception $e) { 355 $this->assertInstanceOf('dml_exception', $e); 356 } 357 358 // Too few params in sql. 359 $sql = "SELECT * FROM {{$tablename}} WHERE name = ?, course = ?, id = ?"; 360 $params = array('record2', 3); 361 try { 362 $DB->fix_sql_params($sql, $params); 363 $this->fail("Expecting an exception, none occurred"); 364 } catch (moodle_exception $e) { 365 $this->assertInstanceOf('dml_exception', $e); 366 } 367 368 // Too many params in array: no error, just use what is necessary. 369 $params[] = 1; 370 $params[] = time(); 371 $sqlarray = $DB->fix_sql_params($sql, $params); 372 $this->assertInternalType('array', $sqlarray); 373 $this->assertCount(3, $sqlarray[1]); 374 375 // Named params missing from array. 376 $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :course"; 377 $params = array('wrongname' => 'record1', 'course' => 1); 378 try { 379 $DB->fix_sql_params($sql, $params); 380 $this->fail("Expecting an exception, none occurred"); 381 } catch (moodle_exception $e) { 382 $this->assertInstanceOf('dml_exception', $e); 383 } 384 385 // Duplicate named param in query - this is a very important feature!! 386 // it helps with debugging of sloppy code. 387 $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :name"; 388 $params = array('name' => 'record2', 'course' => 3); 389 try { 390 $DB->fix_sql_params($sql, $params); 391 $this->fail("Expecting an exception, none occurred"); 392 } catch (moodle_exception $e) { 393 $this->assertInstanceOf('dml_exception', $e); 394 } 395 396 // Extra named param is ignored. 397 $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :course"; 398 $params = array('name' => 'record1', 'course' => 1, 'extrastuff'=>'haha'); 399 $sqlarray = $DB->fix_sql_params($sql, $params); 400 $this->assertInternalType('array', $sqlarray); 401 $this->assertCount(2, $sqlarray[1]); 402 403 // Params exceeding 30 chars length. 404 $sql = "SELECT * FROM {{$tablename}} WHERE name = :long_placeholder_with_more_than_30"; 405 $params = array('long_placeholder_with_more_than_30' => 'record1'); 406 try { 407 $DB->fix_sql_params($sql, $params); 408 $this->fail("Expecting an exception, none occurred"); 409 } catch (moodle_exception $e) { 410 $this->assertInstanceOf('coding_exception', $e); 411 } 412 413 // Booleans in NAMED params are casting to 1/0 int. 414 $sql = "SELECT * FROM {{$tablename}} WHERE course = ? OR course = ?"; 415 $params = array(true, false); 416 list($sql, $params) = $DB->fix_sql_params($sql, $params); 417 $this->assertTrue(reset($params) === 1); 418 $this->assertTrue(next($params) === 0); 419 420 // Booleans in QM params are casting to 1/0 int. 421 $sql = "SELECT * FROM {{$tablename}} WHERE course = :course1 OR course = :course2"; 422 $params = array('course1' => true, 'course2' => false); 423 list($sql, $params) = $DB->fix_sql_params($sql, $params); 424 $this->assertTrue(reset($params) === 1); 425 $this->assertTrue(next($params) === 0); 426 427 // Booleans in DOLLAR params are casting to 1/0 int. 428 $sql = "SELECT * FROM {{$tablename}} WHERE course = \$1 OR course = \$2"; 429 $params = array(true, false); 430 list($sql, $params) = $DB->fix_sql_params($sql, $params); 431 $this->assertTrue(reset($params) === 1); 432 $this->assertTrue(next($params) === 0); 433 434 // No data types are touched except bool. 435 $sql = "SELECT * FROM {{$tablename}} WHERE name IN (?,?,?,?,?,?)"; 436 $inparams = array('abc', 'ABC', null, '1', 1, 1.4); 437 list($sql, $params) = $DB->fix_sql_params($sql, $inparams); 438 $this->assertSame(array_values($params), array_values($inparams)); 439 } 440 441 public function test_strtok() { 442 // Strtok was previously used by bound emulation, make sure it is not used any more. 443 $DB = $this->tdb; 444 $dbman = $this->tdb->get_manager(); 445 446 $table = $this->get_test_table(); 447 $tablename = $table->getName(); 448 449 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 450 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 451 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala'); 452 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 453 $dbman->create_table($table); 454 455 $str = 'a?b?c?d'; 456 $this->assertSame(strtok($str, '?'), 'a'); 457 458 $DB->get_records($tablename, array('id'=>1)); 459 460 $this->assertSame(strtok('?'), 'b'); 461 } 462 463 public function test_tweak_param_names() { 464 // Note the tweak_param_names() method is only available in the oracle driver, 465 // hence we look for expected results indirectly, by testing various DML methods. 466 // with some "extreme" conditions causing the tweak to happen. 467 $DB = $this->tdb; 468 $dbman = $this->tdb->get_manager(); 469 470 $table = $this->get_test_table(); 471 $tablename = $table->getName(); 472 473 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 474 // Add some columns with 28 chars in the name. 475 $table->add_field('long_int_columnname_with_28c', XMLDB_TYPE_INTEGER, '10'); 476 $table->add_field('long_dec_columnname_with_28c', XMLDB_TYPE_NUMBER, '10,2'); 477 $table->add_field('long_str_columnname_with_28c', XMLDB_TYPE_CHAR, '100'); 478 // Add some columns with 30 chars in the name. 479 $table->add_field('long_int_columnname_with_30cxx', XMLDB_TYPE_INTEGER, '10'); 480 $table->add_field('long_dec_columnname_with_30cxx', XMLDB_TYPE_NUMBER, '10,2'); 481 $table->add_field('long_str_columnname_with_30cxx', XMLDB_TYPE_CHAR, '100'); 482 483 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 484 485 $dbman->create_table($table); 486 487 $this->assertTrue($dbman->table_exists($tablename)); 488 489 // Test insert record. 490 $rec1 = new stdClass(); 491 $rec1->long_int_columnname_with_28c = 28; 492 $rec1->long_dec_columnname_with_28c = 28.28; 493 $rec1->long_str_columnname_with_28c = '28'; 494 $rec1->long_int_columnname_with_30cxx = 30; 495 $rec1->long_dec_columnname_with_30cxx = 30.30; 496 $rec1->long_str_columnname_with_30cxx = '30'; 497 498 // Insert_record(). 499 $rec1->id = $DB->insert_record($tablename, $rec1); 500 $this->assertEquals($rec1, $DB->get_record($tablename, array('id' => $rec1->id))); 501 502 // Update_record(). 503 $DB->update_record($tablename, $rec1); 504 $this->assertEquals($rec1, $DB->get_record($tablename, array('id' => $rec1->id))); 505 506 // Set_field(). 507 $rec1->long_int_columnname_with_28c = 280; 508 $DB->set_field($tablename, 'long_int_columnname_with_28c', $rec1->long_int_columnname_with_28c, 509 array('id' => $rec1->id, 'long_int_columnname_with_28c' => 28)); 510 $rec1->long_dec_columnname_with_28c = 280.28; 511 $DB->set_field($tablename, 'long_dec_columnname_with_28c', $rec1->long_dec_columnname_with_28c, 512 array('id' => $rec1->id, 'long_dec_columnname_with_28c' => 28.28)); 513 $rec1->long_str_columnname_with_28c = '280'; 514 $DB->set_field($tablename, 'long_str_columnname_with_28c', $rec1->long_str_columnname_with_28c, 515 array('id' => $rec1->id, 'long_str_columnname_with_28c' => '28')); 516 $rec1->long_int_columnname_with_30cxx = 300; 517 $DB->set_field($tablename, 'long_int_columnname_with_30cxx', $rec1->long_int_columnname_with_30cxx, 518 array('id' => $rec1->id, 'long_int_columnname_with_30cxx' => 30)); 519 $rec1->long_dec_columnname_with_30cxx = 300.30; 520 $DB->set_field($tablename, 'long_dec_columnname_with_30cxx', $rec1->long_dec_columnname_with_30cxx, 521 array('id' => $rec1->id, 'long_dec_columnname_with_30cxx' => 30.30)); 522 $rec1->long_str_columnname_with_30cxx = '300'; 523 $DB->set_field($tablename, 'long_str_columnname_with_30cxx', $rec1->long_str_columnname_with_30cxx, 524 array('id' => $rec1->id, 'long_str_columnname_with_30cxx' => '30')); 525 $this->assertEquals($rec1, $DB->get_record($tablename, array('id' => $rec1->id))); 526 527 // Delete_records(). 528 $rec2 = $DB->get_record($tablename, array('id' => $rec1->id)); 529 $rec2->id = $DB->insert_record($tablename, $rec2); 530 $this->assertEquals(2, $DB->count_records($tablename)); 531 $DB->delete_records($tablename, (array) $rec2); 532 $this->assertEquals(1, $DB->count_records($tablename)); 533 534 // Get_recordset(). 535 $rs = $DB->get_recordset($tablename, (array) $rec1); 536 $iterations = 0; 537 foreach ($rs as $rec2) { 538 $iterations++; 539 } 540 $rs->close(); 541 $this->assertEquals(1, $iterations); 542 $this->assertEquals($rec1, $rec2); 543 544 // Get_records(). 545 $recs = $DB->get_records($tablename, (array) $rec1); 546 $this->assertCount(1, $recs); 547 $this->assertEquals($rec1, reset($recs)); 548 549 // Get_fieldset_select(). 550 $select = 'id = :id AND 551 long_int_columnname_with_28c = :long_int_columnname_with_28c AND 552 long_dec_columnname_with_28c = :long_dec_columnname_with_28c AND 553 long_str_columnname_with_28c = :long_str_columnname_with_28c AND 554 long_int_columnname_with_30cxx = :long_int_columnname_with_30cxx AND 555 long_dec_columnname_with_30cxx = :long_dec_columnname_with_30cxx AND 556 long_str_columnname_with_30cxx = :long_str_columnname_with_30cxx'; 557 $fields = $DB->get_fieldset_select($tablename, 'long_int_columnname_with_28c', $select, (array)$rec1); 558 $this->assertCount(1, $fields); 559 $this->assertEquals($rec1->long_int_columnname_with_28c, reset($fields)); 560 $fields = $DB->get_fieldset_select($tablename, 'long_dec_columnname_with_28c', $select, (array)$rec1); 561 $this->assertEquals($rec1->long_dec_columnname_with_28c, reset($fields)); 562 $fields = $DB->get_fieldset_select($tablename, 'long_str_columnname_with_28c', $select, (array)$rec1); 563 $this->assertEquals($rec1->long_str_columnname_with_28c, reset($fields)); 564 $fields = $DB->get_fieldset_select($tablename, 'long_int_columnname_with_30cxx', $select, (array)$rec1); 565 $this->assertEquals($rec1->long_int_columnname_with_30cxx, reset($fields)); 566 $fields = $DB->get_fieldset_select($tablename, 'long_dec_columnname_with_30cxx', $select, (array)$rec1); 567 $this->assertEquals($rec1->long_dec_columnname_with_30cxx, reset($fields)); 568 $fields = $DB->get_fieldset_select($tablename, 'long_str_columnname_with_30cxx', $select, (array)$rec1); 569 $this->assertEquals($rec1->long_str_columnname_with_30cxx, reset($fields)); 570 571 // Overlapping placeholders (progressive str_replace). 572 $overlapselect = 'id = :p AND 573 long_int_columnname_with_28c = :param1 AND 574 long_dec_columnname_with_28c = :param2 AND 575 long_str_columnname_with_28c = :param_with_29_characters_long AND 576 long_int_columnname_with_30cxx = :param_with_30_characters_long_ AND 577 long_dec_columnname_with_30cxx = :param_ AND 578 long_str_columnname_with_30cxx = :param__'; 579 $overlapparams = array( 580 'p' => $rec1->id, 581 'param1' => $rec1->long_int_columnname_with_28c, 582 'param2' => $rec1->long_dec_columnname_with_28c, 583 'param_with_29_characters_long' => $rec1->long_str_columnname_with_28c, 584 'param_with_30_characters_long_' => $rec1->long_int_columnname_with_30cxx, 585 'param_' => $rec1->long_dec_columnname_with_30cxx, 586 'param__' => $rec1->long_str_columnname_with_30cxx); 587 $recs = $DB->get_records_select($tablename, $overlapselect, $overlapparams); 588 $this->assertCount(1, $recs); 589 $this->assertEquals($rec1, reset($recs)); 590 591 // Execute(). 592 $DB->execute("DELETE FROM {{$tablename}} WHERE $select", (array)$rec1); 593 $this->assertEquals(0, $DB->count_records($tablename)); 594 } 595 596 public function test_get_tables() { 597 $DB = $this->tdb; 598 $dbman = $this->tdb->get_manager(); 599 600 // Need to test with multiple DBs. 601 $table = $this->get_test_table(); 602 $tablename = $table->getName(); 603 604 $original_count = count($DB->get_tables()); 605 606 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 607 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 608 609 $dbman->create_table($table); 610 $this->assertTrue(count($DB->get_tables()) == $original_count + 1); 611 612 $dbman->drop_table($table); 613 $this->assertTrue(count($DB->get_tables()) == $original_count); 614 } 615 616 public function test_get_indexes() { 617 $DB = $this->tdb; 618 $dbman = $this->tdb->get_manager(); 619 620 $table = $this->get_test_table(); 621 $tablename = $table->getName(); 622 623 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 624 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 625 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 626 $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course')); 627 $table->add_index('course-id', XMLDB_INDEX_UNIQUE, array('course', 'id')); 628 $dbman->create_table($table); 629 630 $indices = $DB->get_indexes($tablename); 631 $this->assertInternalType('array', $indices); 632 $this->assertCount(2, $indices); 633 // We do not care about index names for now. 634 $first = array_shift($indices); 635 $second = array_shift($indices); 636 if (count($first['columns']) == 2) { 637 $composed = $first; 638 $single = $second; 639 } else { 640 $composed = $second; 641 $single = $first; 642 } 643 $this->assertFalse($single['unique']); 644 $this->assertTrue($composed['unique']); 645 $this->assertCount(1, $single['columns']); 646 $this->assertCount(2, $composed['columns']); 647 $this->assertSame('course', $single['columns'][0]); 648 $this->assertSame('course', $composed['columns'][0]); 649 $this->assertSame('id', $composed['columns'][1]); 650 } 651 652 public function test_get_columns() { 653 $DB = $this->tdb; 654 $dbman = $this->tdb->get_manager(); 655 656 $table = $this->get_test_table(); 657 $tablename = $table->getName(); 658 659 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 660 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 661 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala'); 662 $table->add_field('description', XMLDB_TYPE_TEXT, 'small', null, null, null, null); 663 $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 664 $table->add_field('oneintnodefault', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null); 665 $table->add_field('enumfield', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'test2'); 666 $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200); 667 $table->add_field('onenumnodefault', XMLDB_TYPE_NUMBER, '10,2', null, null, null); 668 $table->add_field('onefloat', XMLDB_TYPE_FLOAT, '10,2', null, XMLDB_NOTNULL, null, 300); 669 $table->add_field('onefloatnodefault', XMLDB_TYPE_FLOAT, '10,2', null, XMLDB_NOTNULL, null); 670 $table->add_field('anotherfloat', XMLDB_TYPE_FLOAT, null, null, null, null, 400); 671 $table->add_field('negativedfltint', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '-1'); 672 $table->add_field('negativedfltnumber', XMLDB_TYPE_NUMBER, '10', null, XMLDB_NOTNULL, null, '-2'); 673 $table->add_field('negativedfltfloat', XMLDB_TYPE_FLOAT, '10', null, XMLDB_NOTNULL, null, '-3'); 674 $table->add_field('someint1', XMLDB_TYPE_INTEGER, '1', null, null, null, '0'); 675 $table->add_field('someint2', XMLDB_TYPE_INTEGER, '2', null, null, null, '0'); 676 $table->add_field('someint3', XMLDB_TYPE_INTEGER, '3', null, null, null, '0'); 677 $table->add_field('someint4', XMLDB_TYPE_INTEGER, '4', null, null, null, '0'); 678 $table->add_field('someint5', XMLDB_TYPE_INTEGER, '5', null, null, null, '0'); 679 $table->add_field('someint6', XMLDB_TYPE_INTEGER, '6', null, null, null, '0'); 680 $table->add_field('someint7', XMLDB_TYPE_INTEGER, '7', null, null, null, '0'); 681 $table->add_field('someint8', XMLDB_TYPE_INTEGER, '8', null, null, null, '0'); 682 $table->add_field('someint9', XMLDB_TYPE_INTEGER, '9', null, null, null, '0'); 683 $table->add_field('someint10', XMLDB_TYPE_INTEGER, '10', null, null, null, '0'); 684 $table->add_field('someint18', XMLDB_TYPE_INTEGER, '18', null, null, null, '0'); 685 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 686 $dbman->create_table($table); 687 688 $columns = $DB->get_columns($tablename); 689 $this->assertInternalType('array', $columns); 690 691 $fields = $table->getFields(); 692 $this->assertCount(count($columns), $fields); 693 694 $field = $columns['id']; 695 $this->assertSame('R', $field->meta_type); 696 $this->assertTrue($field->auto_increment); 697 $this->assertTrue($field->unique); 698 699 $field = $columns['course']; 700 $this->assertSame('I', $field->meta_type); 701 $this->assertFalse($field->auto_increment); 702 $this->assertTrue($field->has_default); 703 $this->assertEquals(0, $field->default_value); 704 $this->assertTrue($field->not_null); 705 706 for ($i=1; $i<=10; $i++) { 707 $field = $columns['someint'.$i]; 708 $this->assertSame('I', $field->meta_type); 709 $this->assertGreaterThanOrEqual($i, $field->max_length); 710 } 711 $field = $columns['someint18']; 712 $this->assertSame('I', $field->meta_type); 713 $this->assertGreaterThanOrEqual(18, $field->max_length); 714 715 $field = $columns['name']; 716 $this->assertSame('C', $field->meta_type); 717 $this->assertFalse($field->auto_increment); 718 $this->assertEquals(255, $field->max_length); 719 $this->assertTrue($field->has_default); 720 $this->assertSame('lala', $field->default_value); 721 $this->assertFalse($field->not_null); 722 723 $field = $columns['description']; 724 $this->assertSame('X', $field->meta_type); 725 $this->assertFalse($field->auto_increment); 726 $this->assertFalse($field->has_default); 727 $this->assertNull($field->default_value); 728 $this->assertFalse($field->not_null); 729 730 $field = $columns['oneint']; 731 $this->assertSame('I', $field->meta_type); 732 $this->assertFalse($field->auto_increment); 733 $this->assertTrue($field->has_default); 734 $this->assertEquals(0, $field->default_value); 735 $this->assertTrue($field->not_null); 736 737 $field = $columns['oneintnodefault']; 738 $this->assertSame('I', $field->meta_type); 739 $this->assertFalse($field->auto_increment); 740 $this->assertFalse($field->has_default); 741 $this->assertNull($field->default_value); 742 $this->assertTrue($field->not_null); 743 744 $field = $columns['enumfield']; 745 $this->assertSame('C', $field->meta_type); 746 $this->assertFalse($field->auto_increment); 747 $this->assertSame('test2', $field->default_value); 748 $this->assertTrue($field->not_null); 749 750 $field = $columns['onenum']; 751 $this->assertSame('N', $field->meta_type); 752 $this->assertFalse($field->auto_increment); 753 $this->assertEquals(10, $field->max_length); 754 $this->assertEquals(2, $field->scale); 755 $this->assertTrue($field->has_default); 756 $this->assertEquals(200.0, $field->default_value); 757 $this->assertFalse($field->not_null); 758 759 $field = $columns['onenumnodefault']; 760 $this->assertSame('N', $field->meta_type); 761 $this->assertFalse($field->auto_increment); 762 $this->assertEquals(10, $field->max_length); 763 $this->assertEquals(2, $field->scale); 764 $this->assertFalse($field->has_default); 765 $this->assertNull($field->default_value); 766 $this->assertFalse($field->not_null); 767 768 $field = $columns['onefloat']; 769 $this->assertSame('N', $field->meta_type); 770 $this->assertFalse($field->auto_increment); 771 $this->assertTrue($field->has_default); 772 $this->assertEquals(300.0, $field->default_value); 773 $this->assertTrue($field->not_null); 774 775 $field = $columns['onefloatnodefault']; 776 $this->assertSame('N', $field->meta_type); 777 $this->assertFalse($field->auto_increment); 778 $this->assertFalse($field->has_default); 779 $this->assertNull($field->default_value); 780 $this->assertTrue($field->not_null); 781 782 $field = $columns['anotherfloat']; 783 $this->assertSame('N', $field->meta_type); 784 $this->assertFalse($field->auto_increment); 785 $this->assertTrue($field->has_default); 786 $this->assertEquals(400.0, $field->default_value); 787 $this->assertFalse($field->not_null); 788 789 // Test negative defaults in numerical columns. 790 $field = $columns['negativedfltint']; 791 $this->assertTrue($field->has_default); 792 $this->assertEquals(-1, $field->default_value); 793 794 $field = $columns['negativedfltnumber']; 795 $this->assertTrue($field->has_default); 796 $this->assertEquals(-2, $field->default_value); 797 798 $field = $columns['negativedfltfloat']; 799 $this->assertTrue($field->has_default); 800 $this->assertEquals(-3, $field->default_value); 801 802 for ($i = 0; $i < count($columns); $i++) { 803 if ($i == 0) { 804 $next_column = reset($columns); 805 $next_field = reset($fields); 806 } else { 807 $next_column = next($columns); 808 $next_field = next($fields); 809 } 810 811 $this->assertEquals($next_column->name, $next_field->getName()); 812 } 813 814 // Test get_columns for non-existing table returns empty array. MDL-30147. 815 $columns = $DB->get_columns('xxxx'); 816 $this->assertEquals(array(), $columns); 817 818 // Create something similar to "context_temp" with id column without sequence. 819 $dbman->drop_table($table); 820 $table = $this->get_test_table(); 821 $tablename = $table->getName(); 822 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); 823 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 824 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 825 $dbman->create_table($table); 826 827 $columns = $DB->get_columns($tablename); 828 $this->assertFalse($columns['id']->auto_increment); 829 } 830 831 public function test_get_manager() { 832 $DB = $this->tdb; 833 $dbman = $this->tdb->get_manager(); 834 835 $this->assertInstanceOf('database_manager', $dbman); 836 } 837 838 public function test_setup_is_unicodedb() { 839 $DB = $this->tdb; 840 $this->assertTrue($DB->setup_is_unicodedb()); 841 } 842 843 public function test_set_debug() { // Tests get_debug() too. 844 $DB = $this->tdb; 845 $dbman = $this->tdb->get_manager(); 846 847 $table = $this->get_test_table(); 848 $tablename = $table->getName(); 849 850 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 851 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 852 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 853 $dbman->create_table($table); 854 855 $sql = "SELECT * FROM {{$tablename}}"; 856 857 $prevdebug = $DB->get_debug(); 858 859 ob_start(); 860 $DB->set_debug(true); 861 $this->assertTrue($DB->get_debug()); 862 $DB->execute($sql); 863 $DB->set_debug(false); 864 $this->assertFalse($DB->get_debug()); 865 $debuginfo = ob_get_contents(); 866 ob_end_clean(); 867 $this->assertFalse($debuginfo === ''); 868 869 ob_start(); 870 $DB->execute($sql); 871 $debuginfo = ob_get_contents(); 872 ob_end_clean(); 873 $this->assertTrue($debuginfo === ''); 874 875 $DB->set_debug($prevdebug); 876 } 877 878 public function test_execute() { 879 $DB = $this->tdb; 880 $dbman = $this->tdb->get_manager(); 881 882 $table1 = $this->get_test_table('1'); 883 $tablename1 = $table1->getName(); 884 $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 885 $table1->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 886 $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0'); 887 $table1->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course')); 888 $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 889 $dbman->create_table($table1); 890 891 $table2 = $this->get_test_table('2'); 892 $tablename2 = $table2->getName(); 893 $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 894 $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 895 $table2->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null); 896 $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 897 $dbman->create_table($table2); 898 899 $DB->insert_record($tablename1, array('course' => 3, 'name' => 'aaa')); 900 $DB->insert_record($tablename1, array('course' => 1, 'name' => 'bbb')); 901 $DB->insert_record($tablename1, array('course' => 7, 'name' => 'ccc')); 902 $DB->insert_record($tablename1, array('course' => 3, 'name' => 'ddd')); 903 904 // Select results are ignored. 905 $sql = "SELECT * FROM {{$tablename1}} WHERE course = :course"; 906 $this->assertTrue($DB->execute($sql, array('course'=>3))); 907 908 // Throw exception on error. 909 $sql = "XXUPDATE SET XSSD"; 910 try { 911 $DB->execute($sql); 912 $this->fail("Expecting an exception, none occurred"); 913 } catch (moodle_exception $e) { 914 $this->assertInstanceOf('dml_exception', $e); 915 } 916 917 // Update records. 918 $sql = "UPDATE {{$tablename1}} 919 SET course = 6 920 WHERE course = ?"; 921 $this->assertTrue($DB->execute($sql, array('3'))); 922 $this->assertEquals(2, $DB->count_records($tablename1, array('course' => 6))); 923 924 // Update records with subquery condition. 925 // Confirm that the option not using table aliases is cross-db. 926 $sql = "UPDATE {{$tablename1}} 927 SET course = 0 928 WHERE NOT EXISTS ( 929 SELECT course 930 FROM {{$tablename2}} tbl2 931 WHERE tbl2.course = {{$tablename1}}.course 932 AND 1 = 0)"; // Really we don't update anything, but verify the syntax is allowed. 933 $this->assertTrue($DB->execute($sql)); 934 935 // Insert from one into second table. 936 $sql = "INSERT INTO {{$tablename2}} (course) 937 938 SELECT course 939 FROM {{$tablename1}}"; 940 $this->assertTrue($DB->execute($sql)); 941 $this->assertEquals(4, $DB->count_records($tablename2)); 942 943 // Insert a TEXT with raw SQL, binding TEXT params. 944 $course = 9999; 945 $onetext = file_get_contents(__DIR__ . '/fixtures/clob.txt'); 946 $sql = "INSERT INTO {{$tablename2}} (course, onetext) 947 VALUES (:course, :onetext)"; 948 $DB->execute($sql, array('course' => $course, 'onetext' => $onetext)); 949 $records = $DB->get_records($tablename2, array('course' => $course)); 950 $this->assertCount(1, $records); 951 $record = reset($records); 952 $this->assertSame($onetext, $record->onetext); 953 954 // Update a TEXT with raw SQL, binding TEXT params. 955 $newcourse = 10000; 956 $newonetext = file_get_contents(__DIR__ . '/fixtures/clob.txt') . '- updated'; 957 $sql = "UPDATE {{$tablename2}} SET course = :newcourse, onetext = :newonetext 958 WHERE course = :oldcourse"; 959 $DB->execute($sql, array('oldcourse' => $course, 'newcourse' => $newcourse, 'newonetext' => $newonetext)); 960 $records = $DB->get_records($tablename2, array('course' => $course)); 961 $this->assertCount(0, $records); 962 $records = $DB->get_records($tablename2, array('course' => $newcourse)); 963 $this->assertCount(1, $records); 964 $record = reset($records); 965 $this->assertSame($newonetext, $record->onetext); 966 } 967 968 public function test_get_recordset() { 969 $DB = $this->tdb; 970 $dbman = $DB->get_manager(); 971 972 $table = $this->get_test_table(); 973 $tablename = $table->getName(); 974 975 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 976 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 977 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0'); 978 $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null); 979 $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course')); 980 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 981 $dbman->create_table($table); 982 983 $data = array(array('course' => 3, 'name' => 'record1', 'onetext'=>'abc'), 984 array('course' => 3, 'name' => 'record2', 'onetext'=>'abcd'), 985 array('course' => 5, 'name' => 'record3', 'onetext'=>'abcde')); 986 987 foreach ($data as $key => $record) { 988 $data[$key]['id'] = $DB->insert_record($tablename, $record); 989 } 990 991 // Standard recordset iteration. 992 $rs = $DB->get_recordset($tablename); 993 $this->assertInstanceOf('moodle_recordset', $rs); 994 reset($data); 995 foreach ($rs as $record) { 996 $data_record = current($data); 997 foreach ($record as $k => $v) { 998 $this->assertEquals($data_record[$k], $v); 999 } 1000 next($data); 1001 } 1002 $rs->close(); 1003 1004 // Iterator style usage. 1005 $rs = $DB->get_recordset($tablename); 1006 $this->assertInstanceOf('moodle_recordset', $rs); 1007 reset($data); 1008 while ($rs->valid()) { 1009 $record = $rs->current(); 1010 $data_record = current($data); 1011 foreach ($record as $k => $v) { 1012 $this->assertEquals($data_record[$k], $v); 1013 } 1014 next($data); 1015 $rs->next(); 1016 } 1017 $rs->close(); 1018 1019 // Make sure rewind is ignored. 1020 $rs = $DB->get_recordset($tablename); 1021 $this->assertInstanceOf('moodle_recordset', $rs); 1022 reset($data); 1023 $i = 0; 1024 foreach ($rs as $record) { 1025 $i++; 1026 $rs->rewind(); 1027 if ($i > 10) { 1028 $this->fail('revind not ignored in recordsets'); 1029 break; 1030 } 1031 $data_record = current($data); 1032 foreach ($record as $k => $v) { 1033 $this->assertEquals($data_record[$k], $v); 1034 } 1035 next($data); 1036 } 1037 $rs->close(); 1038 1039 // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int). 1040 $conditions = array('onetext' => '1'); 1041 try { 1042 $rs = $DB->get_recordset($tablename, $conditions); 1043 $this->fail('An Exception is missing, expected due to equating of text fields'); 1044 } catch (moodle_exception $e) { 1045 $this->assertInstanceOf('dml_exception', $e); 1046 $this->assertSame('textconditionsnotallowed', $e->errorcode); 1047 } 1048 1049 // Test nested iteration. 1050 $rs1 = $DB->get_recordset($tablename); 1051 $i = 0; 1052 foreach ($rs1 as $record1) { 1053 $rs2 = $DB->get_recordset($tablename); 1054 $i++; 1055 $j = 0; 1056 foreach ($rs2 as $record2) { 1057 $j++; 1058 } 1059 $rs2->close(); 1060 $this->assertCount($j, $data); 1061 } 1062 $rs1->close(); 1063 $this->assertCount($i, $data); 1064 1065 // Notes: 1066 // * limits are tested in test_get_recordset_sql() 1067 // * where_clause() is used internally and is tested in test_get_records() 1068 } 1069 1070 public function test_get_recordset_static() { 1071 $DB = $this->tdb; 1072 $dbman = $DB->get_manager(); 1073 1074 $table = $this->get_test_table(); 1075 $tablename = $table->getName(); 1076 1077 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1078 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1079 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1080 $dbman->create_table($table); 1081 1082 $DB->insert_record($tablename, array('course' => 1)); 1083 $DB->insert_record($tablename, array('course' => 2)); 1084 $DB->insert_record($tablename, array('course' => 3)); 1085 $DB->insert_record($tablename, array('course' => 4)); 1086 1087 $rs = $DB->get_recordset($tablename, array(), 'id'); 1088 1089 $DB->set_field($tablename, 'course', 666, array('course'=>1)); 1090 $DB->delete_records($tablename, array('course'=>2)); 1091 1092 $i = 0; 1093 foreach ($rs as $record) { 1094 $i++; 1095 $this->assertEquals($i, $record->course); 1096 } 1097 $rs->close(); 1098 $this->assertEquals(4, $i); 1099 1100 // Now repeat with limits because it may use different code. 1101 $DB->delete_records($tablename, array()); 1102 1103 $DB->insert_record($tablename, array('course' => 1)); 1104 $DB->insert_record($tablename, array('course' => 2)); 1105 $DB->insert_record($tablename, array('course' => 3)); 1106 $DB->insert_record($tablename, array('course' => 4)); 1107 1108 $rs = $DB->get_recordset($tablename, array(), 'id', '*', 0, 3); 1109 1110 $DB->set_field($tablename, 'course', 666, array('course'=>1)); 1111 $DB->delete_records($tablename, array('course'=>2)); 1112 1113 $i = 0; 1114 foreach ($rs as $record) { 1115 $i++; 1116 $this->assertEquals($i, $record->course); 1117 } 1118 $rs->close(); 1119 $this->assertEquals(3, $i); 1120 } 1121 1122 public function test_get_recordset_iterator_keys() { 1123 $DB = $this->tdb; 1124 $dbman = $DB->get_manager(); 1125 1126 $table = $this->get_test_table(); 1127 $tablename = $table->getName(); 1128 1129 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1130 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1131 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0'); 1132 $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course')); 1133 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1134 $dbman->create_table($table); 1135 1136 $data = array(array('course' => 3, 'name' => 'record1'), 1137 array('course' => 3, 'name' => 'record2'), 1138 array('course' => 5, 'name' => 'record3')); 1139 foreach ($data as $key => $record) { 1140 $data[$key]['id'] = $DB->insert_record($tablename, $record); 1141 } 1142 1143 // Test repeated numeric keys are returned ok. 1144 $rs = $DB->get_recordset($tablename, null, null, 'course, name, id'); 1145 1146 reset($data); 1147 $count = 0; 1148 foreach ($rs as $key => $record) { 1149 $data_record = current($data); 1150 $this->assertEquals($data_record['course'], $key); 1151 next($data); 1152 $count++; 1153 } 1154 $rs->close(); 1155 $this->assertEquals(3, $count); 1156 1157 // Test string keys are returned ok. 1158 $rs = $DB->get_recordset($tablename, null, null, 'name, course, id'); 1159 1160 reset($data); 1161 $count = 0; 1162 foreach ($rs as $key => $record) { 1163 $data_record = current($data); 1164 $this->assertEquals($data_record['name'], $key); 1165 next($data); 1166 $count++; 1167 } 1168 $rs->close(); 1169 $this->assertEquals(3, $count); 1170 1171 // Test numeric not starting in 1 keys are returned ok. 1172 $rs = $DB->get_recordset($tablename, null, 'id DESC', 'id, course, name'); 1173 1174 $data = array_reverse($data); 1175 reset($data); 1176 $count = 0; 1177 foreach ($rs as $key => $record) { 1178 $data_record = current($data); 1179 $this->assertEquals($data_record['id'], $key); 1180 next($data); 1181 $count++; 1182 } 1183 $rs->close(); 1184 $this->assertEquals(3, $count); 1185 } 1186 1187 public function test_get_recordset_list() { 1188 $DB = $this->tdb; 1189 $dbman = $DB->get_manager(); 1190 1191 $table = $this->get_test_table(); 1192 $tablename = $table->getName(); 1193 1194 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1195 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, null, null, '0'); 1196 $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course')); 1197 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1198 $dbman->create_table($table); 1199 1200 $DB->insert_record($tablename, array('course' => 3)); 1201 $DB->insert_record($tablename, array('course' => 3)); 1202 $DB->insert_record($tablename, array('course' => 5)); 1203 $DB->insert_record($tablename, array('course' => 2)); 1204 $DB->insert_record($tablename, array('course' => null)); 1205 $DB->insert_record($tablename, array('course' => 1)); 1206 $DB->insert_record($tablename, array('course' => 0)); 1207 1208 $rs = $DB->get_recordset_list($tablename, 'course', array(3, 2)); 1209 $counter = 0; 1210 foreach ($rs as $record) { 1211 $counter++; 1212 } 1213 $this->assertEquals(3, $counter); 1214 $rs->close(); 1215 1216 $rs = $DB->get_recordset_list($tablename, 'course', array(3)); 1217 $counter = 0; 1218 foreach ($rs as $record) { 1219 $counter++; 1220 } 1221 $this->assertEquals(2, $counter); 1222 $rs->close(); 1223 1224 $rs = $DB->get_recordset_list($tablename, 'course', array(null)); 1225 $counter = 0; 1226 foreach ($rs as $record) { 1227 $counter++; 1228 } 1229 $this->assertEquals(1, $counter); 1230 $rs->close(); 1231 1232 $rs = $DB->get_recordset_list($tablename, 'course', array(6, null)); 1233 $counter = 0; 1234 foreach ($rs as $record) { 1235 $counter++; 1236 } 1237 $this->assertEquals(1, $counter); 1238 $rs->close(); 1239 1240 $rs = $DB->get_recordset_list($tablename, 'course', array(null, 5, 5, 5)); 1241 $counter = 0; 1242 foreach ($rs as $record) { 1243 $counter++; 1244 } 1245 $this->assertEquals(2, $counter); 1246 $rs->close(); 1247 1248 $rs = $DB->get_recordset_list($tablename, 'course', array(true)); 1249 $counter = 0; 1250 foreach ($rs as $record) { 1251 $counter++; 1252 } 1253 $this->assertEquals(1, $counter); 1254 $rs->close(); 1255 1256 $rs = $DB->get_recordset_list($tablename, 'course', array(false)); 1257 $counter = 0; 1258 foreach ($rs as $record) { 1259 $counter++; 1260 } 1261 $this->assertEquals(1, $counter); 1262 $rs->close(); 1263 1264 $rs = $DB->get_recordset_list($tablename, 'course', array()); // Must return 0 rows without conditions. MDL-17645. 1265 1266 $counter = 0; 1267 foreach ($rs as $record) { 1268 $counter++; 1269 } 1270 $rs->close(); 1271 $this->assertEquals(0, $counter); 1272 1273 // Notes: 1274 // * limits are tested in test_get_recordset_sql() 1275 // * where_clause() is used internally and is tested in test_get_records() 1276 } 1277 1278 public function test_get_recordset_select() { 1279 $DB = $this->tdb; 1280 $dbman = $DB->get_manager(); 1281 1282 $table = $this->get_test_table(); 1283 $tablename = $table->getName(); 1284 1285 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1286 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1287 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1288 $dbman->create_table($table); 1289 1290 $DB->insert_record($tablename, array('course' => 3)); 1291 $DB->insert_record($tablename, array('course' => 3)); 1292 $DB->insert_record($tablename, array('course' => 5)); 1293 $DB->insert_record($tablename, array('course' => 2)); 1294 1295 $rs = $DB->get_recordset_select($tablename, ''); 1296 $counter = 0; 1297 foreach ($rs as $record) { 1298 $counter++; 1299 } 1300 $rs->close(); 1301 $this->assertEquals(4, $counter); 1302 1303 $this->assertNotEmpty($rs = $DB->get_recordset_select($tablename, 'course = 3')); 1304 $counter = 0; 1305 foreach ($rs as $record) { 1306 $counter++; 1307 } 1308 $rs->close(); 1309 $this->assertEquals(2, $counter); 1310 1311 // Notes: 1312 // * limits are tested in test_get_recordset_sql() 1313 } 1314 1315 public function test_get_recordset_sql() { 1316 $DB = $this->tdb; 1317 $dbman = $DB->get_manager(); 1318 1319 $table = $this->get_test_table(); 1320 $tablename = $table->getName(); 1321 1322 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1323 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1324 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1325 $dbman->create_table($table); 1326 1327 $inskey1 = $DB->insert_record($tablename, array('course' => 3)); 1328 $inskey2 = $DB->insert_record($tablename, array('course' => 5)); 1329 $inskey3 = $DB->insert_record($tablename, array('course' => 4)); 1330 $inskey4 = $DB->insert_record($tablename, array('course' => 3)); 1331 $inskey5 = $DB->insert_record($tablename, array('course' => 2)); 1332 $inskey6 = $DB->insert_record($tablename, array('course' => 1)); 1333 $inskey7 = $DB->insert_record($tablename, array('course' => 0)); 1334 1335 $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)); 1336 $counter = 0; 1337 foreach ($rs as $record) { 1338 $counter++; 1339 } 1340 $rs->close(); 1341 $this->assertEquals(2, $counter); 1342 1343 // Limits - only need to test this case, the rest have been tested by test_get_records_sql() 1344 // only limitfrom = skips that number of records. 1345 $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0); 1346 $records = array(); 1347 foreach ($rs as $key => $record) { 1348 $records[$key] = $record; 1349 } 1350 $rs->close(); 1351 $this->assertCount(5, $records); 1352 $this->assertEquals($inskey3, reset($records)->id); 1353 $this->assertEquals($inskey7, end($records)->id); 1354 1355 // Note: fetching nulls, empties, LOBs already tested by test_insert_record() no needed here. 1356 } 1357 1358 public function test_export_table_recordset() { 1359 $DB = $this->tdb; 1360 $dbman = $DB->get_manager(); 1361 1362 $table = $this->get_test_table(); 1363 $tablename = $table->getName(); 1364 1365 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1366 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1367 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1368 $dbman->create_table($table); 1369 1370 $ids = array(); 1371 $ids[] = $DB->insert_record($tablename, array('course' => 3)); 1372 $ids[] = $DB->insert_record($tablename, array('course' => 5)); 1373 $ids[] = $DB->insert_record($tablename, array('course' => 4)); 1374 $ids[] = $DB->insert_record($tablename, array('course' => 3)); 1375 $ids[] = $DB->insert_record($tablename, array('course' => 2)); 1376 $ids[] = $DB->insert_record($tablename, array('course' => 1)); 1377 $ids[] = $DB->insert_record($tablename, array('course' => 0)); 1378 1379 $rs = $DB->export_table_recordset($tablename); 1380 $rids = array(); 1381 foreach ($rs as $record) { 1382 $rids[] = $record->id; 1383 } 1384 $rs->close(); 1385 $this->assertEquals($ids, $rids, '', 0, 0, true); 1386 } 1387 1388 public function test_get_records() { 1389 $DB = $this->tdb; 1390 $dbman = $DB->get_manager(); 1391 1392 $table = $this->get_test_table(); 1393 $tablename = $table->getName(); 1394 1395 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1396 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1397 $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null); 1398 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1399 $dbman->create_table($table); 1400 1401 $DB->insert_record($tablename, array('course' => 3)); 1402 $DB->insert_record($tablename, array('course' => 3)); 1403 $DB->insert_record($tablename, array('course' => 5)); 1404 $DB->insert_record($tablename, array('course' => 2)); 1405 1406 // All records. 1407 $records = $DB->get_records($tablename); 1408 $this->assertCount(4, $records); 1409 $this->assertEquals(3, $records[1]->course); 1410 $this->assertEquals(3, $records[2]->course); 1411 $this->assertEquals(5, $records[3]->course); 1412 $this->assertEquals(2, $records[4]->course); 1413 1414 // Records matching certain conditions. 1415 $records = $DB->get_records($tablename, array('course' => 3)); 1416 $this->assertCount(2, $records); 1417 $this->assertEquals(3, $records[1]->course); 1418 $this->assertEquals(3, $records[2]->course); 1419 1420 // All records sorted by course. 1421 $records = $DB->get_records($tablename, null, 'course'); 1422 $this->assertCount(4, $records); 1423 $current_record = reset($records); 1424 $this->assertEquals(4, $current_record->id); 1425 $current_record = next($records); 1426 $this->assertEquals(1, $current_record->id); 1427 $current_record = next($records); 1428 $this->assertEquals(2, $current_record->id); 1429 $current_record = next($records); 1430 $this->assertEquals(3, $current_record->id); 1431 1432 // All records, but get only one field. 1433 $records = $DB->get_records($tablename, null, '', 'id'); 1434 $this->assertFalse(isset($records[1]->course)); 1435 $this->assertTrue(isset($records[1]->id)); 1436 $this->assertCount(4, $records); 1437 1438 // Booleans into params. 1439 $records = $DB->get_records($tablename, array('course' => true)); 1440 $this->assertCount(0, $records); 1441 $records = $DB->get_records($tablename, array('course' => false)); 1442 $this->assertCount(0, $records); 1443 1444 // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int). 1445 $conditions = array('onetext' => '1'); 1446 try { 1447 $records = $DB->get_records($tablename, $conditions); 1448 if (debugging()) { 1449 // Only in debug mode - hopefully all devs test code in debug mode... 1450 $this->fail('An Exception is missing, expected due to equating of text fields'); 1451 } 1452 } catch (moodle_exception $e) { 1453 $this->assertInstanceOf('dml_exception', $e); 1454 $this->assertSame('textconditionsnotallowed', $e->errorcode); 1455 } 1456 1457 // Test get_records passing non-existing table. 1458 // with params. 1459 try { 1460 $records = $DB->get_records('xxxx', array('id' => 0)); 1461 $this->fail('An Exception is missing, expected due to query against non-existing table'); 1462 } catch (moodle_exception $e) { 1463 $this->assertInstanceOf('dml_exception', $e); 1464 if (debugging()) { 1465 // Information for developers only, normal users get general error message. 1466 $this->assertSame('ddltablenotexist', $e->errorcode); 1467 } 1468 } 1469 1470 try { 1471 $records = $DB->get_records('xxxx', array('id' => '1')); 1472 $this->fail('An Exception is missing, expected due to query against non-existing table'); 1473 } catch (moodle_exception $e) { 1474 $this->assertInstanceOf('dml_exception', $e); 1475 if (debugging()) { 1476 // Information for developers only, normal users get general error message. 1477 $this->assertSame('ddltablenotexist', $e->errorcode); 1478 } 1479 } 1480 1481 // Test get_records passing non-existing column. 1482 try { 1483 $records = $DB->get_records($tablename, array('xxxx' => 0)); 1484 $this->fail('An Exception is missing, expected due to query against non-existing column'); 1485 } catch (moodle_exception $e) { 1486 $this->assertInstanceOf('dml_exception', $e); 1487 if (debugging()) { 1488 // Information for developers only, normal users get general error message. 1489 $this->assertSame('ddlfieldnotexist', $e->errorcode); 1490 } 1491 } 1492 1493 // Note: delegate limits testing to test_get_records_sql(). 1494 } 1495 1496 public function test_get_records_list() { 1497 $DB = $this->tdb; 1498 $dbman = $DB->get_manager(); 1499 1500 $table = $this->get_test_table(); 1501 $tablename = $table->getName(); 1502 1503 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1504 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1505 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1506 $dbman->create_table($table); 1507 1508 $DB->insert_record($tablename, array('course' => 3)); 1509 $DB->insert_record($tablename, array('course' => 3)); 1510 $DB->insert_record($tablename, array('course' => 5)); 1511 $DB->insert_record($tablename, array('course' => 2)); 1512 1513 $records = $DB->get_records_list($tablename, 'course', array(3, 2)); 1514 $this->assertInternalType('array', $records); 1515 $this->assertCount(3, $records); 1516 $this->assertEquals(1, reset($records)->id); 1517 $this->assertEquals(2, next($records)->id); 1518 $this->assertEquals(4, next($records)->id); 1519 1520 $this->assertSame(array(), $records = $DB->get_records_list($tablename, 'course', array())); // Must return 0 rows without conditions. MDL-17645. 1521 $this->assertCount(0, $records); 1522 1523 // Note: delegate limits testing to test_get_records_sql(). 1524 } 1525 1526 public function test_get_records_sql() { 1527 $DB = $this->tdb; 1528 $dbman = $DB->get_manager(); 1529 1530 $table = $this->get_test_table(); 1531 $tablename = $table->getName(); 1532 1533 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1534 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1535 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1536 $dbman->create_table($table); 1537 1538 $inskey1 = $DB->insert_record($tablename, array('course' => 3)); 1539 $inskey2 = $DB->insert_record($tablename, array('course' => 5)); 1540 $inskey3 = $DB->insert_record($tablename, array('course' => 4)); 1541 $inskey4 = $DB->insert_record($tablename, array('course' => 3)); 1542 $inskey5 = $DB->insert_record($tablename, array('course' => 2)); 1543 $inskey6 = $DB->insert_record($tablename, array('course' => 1)); 1544 $inskey7 = $DB->insert_record($tablename, array('course' => 0)); 1545 1546 $table2 = $this->get_test_table("2"); 1547 $tablename2 = $table2->getName(); 1548 $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1549 $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1550 $table2->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null); 1551 $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1552 $dbman->create_table($table2); 1553 1554 $DB->insert_record($tablename2, array('course'=>3, 'nametext'=>'badabing')); 1555 $DB->insert_record($tablename2, array('course'=>4, 'nametext'=>'badabang')); 1556 $DB->insert_record($tablename2, array('course'=>5, 'nametext'=>'badabung')); 1557 $DB->insert_record($tablename2, array('course'=>6, 'nametext'=>'badabong')); 1558 1559 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)); 1560 $this->assertCount(2, $records); 1561 $this->assertEquals($inskey1, reset($records)->id); 1562 $this->assertEquals($inskey4, next($records)->id); 1563 1564 // Awful test, requires debug enabled and sent to browser. Let's do that and restore after test. 1565 $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null); 1566 $this->assertDebuggingCalled(); 1567 $this->assertCount(6, $records); 1568 set_debugging(DEBUG_MINIMAL); 1569 $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null); 1570 $this->assertDebuggingNotCalled(); 1571 $this->assertCount(6, $records); 1572 set_debugging(DEBUG_DEVELOPER); 1573 1574 // Negative limits = no limits. 1575 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, -1, -1); 1576 $this->assertCount(7, $records); 1577 1578 // Zero limits = no limits. 1579 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 0); 1580 $this->assertCount(7, $records); 1581 1582 // Only limitfrom = skips that number of records. 1583 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0); 1584 $this->assertCount(5, $records); 1585 $this->assertEquals($inskey3, reset($records)->id); 1586 $this->assertEquals($inskey7, end($records)->id); 1587 1588 // Only limitnum = fetches that number of records. 1589 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 3); 1590 $this->assertCount(3, $records); 1591 $this->assertEquals($inskey1, reset($records)->id); 1592 $this->assertEquals($inskey3, end($records)->id); 1593 1594 // Both limitfrom and limitnum = skips limitfrom records and fetches limitnum ones. 1595 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 3, 2); 1596 $this->assertCount(2, $records); 1597 $this->assertEquals($inskey4, reset($records)->id); 1598 $this->assertEquals($inskey5, end($records)->id); 1599 1600 // Both limitfrom and limitnum in query having subqueris. 1601 // Note the subquery skips records with course = 0 and 3. 1602 $sql = "SELECT * FROM {{$tablename}} 1603 WHERE course NOT IN ( 1604 SELECT course FROM {{$tablename}} 1605 WHERE course IN (0, 3)) 1606 ORDER BY course"; 1607 $records = $DB->get_records_sql($sql, null, 0, 2); // Skip 0, get 2. 1608 $this->assertCount(2, $records); 1609 $this->assertEquals($inskey6, reset($records)->id); 1610 $this->assertEquals($inskey5, end($records)->id); 1611 $records = $DB->get_records_sql($sql, null, 2, 2); // Skip 2, get 2. 1612 $this->assertCount(2, $records); 1613 $this->assertEquals($inskey3, reset($records)->id); 1614 $this->assertEquals($inskey2, end($records)->id); 1615 1616 // Test 2 tables with aliases and limits with order bys. 1617 $sql = "SELECT t1.id, t1.course AS cid, t2.nametext 1618 FROM {{$tablename}} t1, {{$tablename2}} t2 1619 WHERE t2.course=t1.course 1620 ORDER BY t1.course, ". $DB->sql_compare_text('t2.nametext'); 1621 $records = $DB->get_records_sql($sql, null, 2, 2); // Skip courses 3 and 6, get 4 and 5. 1622 $this->assertCount(2, $records); 1623 $this->assertSame('5', end($records)->cid); 1624 $this->assertSame('4', reset($records)->cid); 1625 1626 // Test 2 tables with aliases and limits with the highest INT limit works. 1627 $records = $DB->get_records_sql($sql, null, 2, PHP_INT_MAX); // Skip course {3,6}, get {4,5}. 1628 $this->assertCount(2, $records); 1629 $this->assertSame('5', end($records)->cid); 1630 $this->assertSame('4', reset($records)->cid); 1631 1632 // Test 2 tables with aliases and limits with order bys (limit which is highest INT number). 1633 $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, 2); // Skip all courses. 1634 $this->assertCount(0, $records); 1635 1636 // Test 2 tables with aliases and limits with order bys (limit which s highest INT number). 1637 $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, PHP_INT_MAX); // Skip all courses. 1638 $this->assertCount(0, $records); 1639 1640 // TODO: Test limits in queries having DISTINCT clauses. 1641 1642 // Note: fetching nulls, empties, LOBs already tested by test_update_record() no needed here. 1643 } 1644 1645 public function test_get_records_menu() { 1646 $DB = $this->tdb; 1647 $dbman = $DB->get_manager(); 1648 1649 $table = $this->get_test_table(); 1650 $tablename = $table->getName(); 1651 1652 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1653 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1654 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1655 $dbman->create_table($table); 1656 1657 $DB->insert_record($tablename, array('course' => 3)); 1658 $DB->insert_record($tablename, array('course' => 3)); 1659 $DB->insert_record($tablename, array('course' => 5)); 1660 $DB->insert_record($tablename, array('course' => 2)); 1661 1662 $records = $DB->get_records_menu($tablename, array('course' => 3)); 1663 $this->assertInternalType('array', $records); 1664 $this->assertCount(2, $records); 1665 $this->assertNotEmpty($records[1]); 1666 $this->assertNotEmpty($records[2]); 1667 $this->assertEquals(3, $records[1]); 1668 $this->assertEquals(3, $records[2]); 1669 1670 // Note: delegate limits testing to test_get_records_sql(). 1671 } 1672 1673 public function test_get_records_select_menu() { 1674 $DB = $this->tdb; 1675 $dbman = $DB->get_manager(); 1676 1677 $table = $this->get_test_table(); 1678 $tablename = $table->getName(); 1679 1680 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1681 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1682 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1683 $dbman->create_table($table); 1684 1685 $DB->insert_record($tablename, array('course' => 3)); 1686 $DB->insert_record($tablename, array('course' => 2)); 1687 $DB->insert_record($tablename, array('course' => 3)); 1688 $DB->insert_record($tablename, array('course' => 5)); 1689 1690 $records = $DB->get_records_select_menu($tablename, "course > ?", array(2)); 1691 $this->assertInternalType('array', $records); 1692 1693 $this->assertCount(3, $records); 1694 $this->assertArrayHasKey(1, $records); 1695 $this->assertArrayNotHasKey(2, $records); 1696 $this->assertArrayHasKey(3, $records); 1697 $this->assertArrayHasKey(4, $records); 1698 $this->assertSame('3', $records[1]); 1699 $this->assertSame('3', $records[3]); 1700 $this->assertSame('5', $records[4]); 1701 1702 // Note: delegate limits testing to test_get_records_sql(). 1703 } 1704 1705 public function test_get_records_sql_menu() { 1706 $DB = $this->tdb; 1707 $dbman = $DB->get_manager(); 1708 1709 $table = $this->get_test_table(); 1710 $tablename = $table->getName(); 1711 1712 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1713 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1714 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1715 $dbman->create_table($table); 1716 1717 $DB->insert_record($tablename, array('course' => 3)); 1718 $DB->insert_record($tablename, array('course' => 2)); 1719 $DB->insert_record($tablename, array('course' => 3)); 1720 $DB->insert_record($tablename, array('course' => 5)); 1721 1722 $records = $DB->get_records_sql_menu("SELECT * FROM {{$tablename}} WHERE course > ?", array(2)); 1723 $this->assertInternalType('array', $records); 1724 1725 $this->assertCount(3, $records); 1726 $this->assertArrayHasKey(1, $records); 1727 $this->assertArrayNotHasKey(2, $records); 1728 $this->assertArrayHasKey(3, $records); 1729 $this->assertArrayHasKey(4, $records); 1730 $this->assertSame('3', $records[1]); 1731 $this->assertSame('3', $records[3]); 1732 $this->assertSame('5', $records[4]); 1733 1734 // Note: delegate limits testing to test_get_records_sql(). 1735 } 1736 1737 public function test_get_record() { 1738 $DB = $this->tdb; 1739 $dbman = $DB->get_manager(); 1740 1741 $table = $this->get_test_table(); 1742 $tablename = $table->getName(); 1743 1744 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1745 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1746 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1747 $dbman->create_table($table); 1748 1749 $DB->insert_record($tablename, array('course' => 3)); 1750 $DB->insert_record($tablename, array('course' => 2)); 1751 1752 $record = $DB->get_record($tablename, array('id' => 2)); 1753 $this->assertInstanceOf('stdClass', $record); 1754 1755 $this->assertEquals(2, $record->course); 1756 $this->assertEquals(2, $record->id); 1757 } 1758 1759 1760 public function test_get_record_select() { 1761 $DB = $this->tdb; 1762 $dbman = $DB->get_manager(); 1763 1764 $table = $this->get_test_table(); 1765 $tablename = $table->getName(); 1766 1767 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1768 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1769 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1770 $dbman->create_table($table); 1771 1772 $DB->insert_record($tablename, array('course' => 3)); 1773 $DB->insert_record($tablename, array('course' => 2)); 1774 1775 $record = $DB->get_record_select($tablename, "id = ?", array(2)); 1776 $this->assertInstanceOf('stdClass', $record); 1777 1778 $this->assertEquals(2, $record->course); 1779 1780 // Note: delegates limit testing to test_get_records_sql(). 1781 } 1782 1783 public function test_get_record_sql() { 1784 $DB = $this->tdb; 1785 $dbman = $DB->get_manager(); 1786 1787 $table = $this->get_test_table(); 1788 $tablename = $table->getName(); 1789 1790 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1791 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1792 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1793 $dbman->create_table($table); 1794 1795 $DB->insert_record($tablename, array('course' => 3)); 1796 $DB->insert_record($tablename, array('course' => 2)); 1797 1798 // Standard use. 1799 $record = $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(2)); 1800 $this->assertInstanceOf('stdClass', $record); 1801 $this->assertEquals(2, $record->course); 1802 $this->assertEquals(2, $record->id); 1803 1804 // Backwards compatibility with $ignoremultiple. 1805 $this->assertFalse((bool)IGNORE_MISSING); 1806 $this->assertTrue((bool)IGNORE_MULTIPLE); 1807 1808 // Record not found - ignore. 1809 $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MISSING)); 1810 $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MULTIPLE)); 1811 1812 // Record not found error. 1813 try { 1814 $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), MUST_EXIST); 1815 $this->fail("Exception expected"); 1816 } catch (dml_missing_record_exception $e) { 1817 $this->assertTrue(true); 1818 } 1819 1820 $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING)); 1821 $this->assertDebuggingCalled(); 1822 set_debugging(DEBUG_MINIMAL); 1823 $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING)); 1824 $this->assertDebuggingNotCalled(); 1825 set_debugging(DEBUG_DEVELOPER); 1826 1827 // Multiple matches ignored. 1828 $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MULTIPLE)); 1829 1830 // Multiple found error. 1831 try { 1832 $DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), MUST_EXIST); 1833 $this->fail("Exception expected"); 1834 } catch (dml_multiple_records_exception $e) { 1835 $this->assertTrue(true); 1836 } 1837 } 1838 1839 public function test_get_field() { 1840 $DB = $this->tdb; 1841 $dbman = $DB->get_manager(); 1842 1843 $table = $this->get_test_table(); 1844 $tablename = $table->getName(); 1845 1846 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1847 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1848 $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null); 1849 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1850 $dbman->create_table($table); 1851 1852 $id1 = $DB->insert_record($tablename, array('course' => 3)); 1853 $DB->insert_record($tablename, array('course' => 5)); 1854 $DB->insert_record($tablename, array('course' => 5)); 1855 1856 $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id1))); 1857 $this->assertEquals(3, $DB->get_field($tablename, 'course', array('course' => 3))); 1858 1859 $this->assertFalse($DB->get_field($tablename, 'course', array('course' => 11), IGNORE_MISSING)); 1860 try { 1861 $DB->get_field($tablename, 'course', array('course' => 4), MUST_EXIST); 1862 $this->fail('Exception expected due to missing record'); 1863 } catch (dml_exception $ex) { 1864 $this->assertTrue(true); 1865 } 1866 1867 $this->assertEquals(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MULTIPLE)); 1868 $this->assertDebuggingNotCalled(); 1869 1870 $this->assertEquals(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MISSING)); 1871 $this->assertDebuggingCalled(); 1872 1873 // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int). 1874 $conditions = array('onetext' => '1'); 1875 try { 1876 $DB->get_field($tablename, 'course', $conditions); 1877 if (debugging()) { 1878 // Only in debug mode - hopefully all devs test code in debug mode... 1879 $this->fail('An Exception is missing, expected due to equating of text fields'); 1880 } 1881 } catch (moodle_exception $e) { 1882 $this->assertInstanceOf('dml_exception', $e); 1883 $this->assertSame('textconditionsnotallowed', $e->errorcode); 1884 } 1885 } 1886 1887 public function test_get_field_select() { 1888 $DB = $this->tdb; 1889 $dbman = $DB->get_manager(); 1890 1891 $table = $this->get_test_table(); 1892 $tablename = $table->getName(); 1893 1894 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1895 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1896 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1897 $dbman->create_table($table); 1898 1899 $DB->insert_record($tablename, array('course' => 3)); 1900 1901 $this->assertEquals(3, $DB->get_field_select($tablename, 'course', "id = ?", array(1))); 1902 } 1903 1904 public function test_get_field_sql() { 1905 $DB = $this->tdb; 1906 $dbman = $DB->get_manager(); 1907 1908 $table = $this->get_test_table(); 1909 $tablename = $table->getName(); 1910 1911 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1912 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1913 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1914 $dbman->create_table($table); 1915 1916 $DB->insert_record($tablename, array('course' => 3)); 1917 1918 $this->assertEquals(3, $DB->get_field_sql("SELECT course FROM {{$tablename}} WHERE id = ?", array(1))); 1919 } 1920 1921 public function test_get_fieldset_select() { 1922 $DB = $this->tdb; 1923 $dbman = $DB->get_manager(); 1924 1925 $table = $this->get_test_table(); 1926 $tablename = $table->getName(); 1927 1928 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1929 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1930 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1931 $dbman->create_table($table); 1932 1933 $DB->insert_record($tablename, array('course' => 1)); 1934 $DB->insert_record($tablename, array('course' => 3)); 1935 $DB->insert_record($tablename, array('course' => 2)); 1936 $DB->insert_record($tablename, array('course' => 6)); 1937 1938 $fieldset = $DB->get_fieldset_select($tablename, 'course', "course > ?", array(1)); 1939 $this->assertInternalType('array', $fieldset); 1940 1941 $this->assertCount(3, $fieldset); 1942 $this->assertEquals(3, $fieldset[0]); 1943 $this->assertEquals(2, $fieldset[1]); 1944 $this->assertEquals(6, $fieldset[2]); 1945 } 1946 1947 public function test_get_fieldset_sql() { 1948 $DB = $this->tdb; 1949 $dbman = $DB->get_manager(); 1950 1951 $table = $this->get_test_table(); 1952 $tablename = $table->getName(); 1953 1954 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1955 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1956 $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null); 1957 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1958 $dbman->create_table($table); 1959 1960 $binarydata = '\\'.chr(241); 1961 1962 $DB->insert_record($tablename, array('course' => 1, 'onebinary' => $binarydata)); 1963 $DB->insert_record($tablename, array('course' => 3, 'onebinary' => $binarydata)); 1964 $DB->insert_record($tablename, array('course' => 2, 'onebinary' => $binarydata)); 1965 $DB->insert_record($tablename, array('course' => 6, 'onebinary' => $binarydata)); 1966 1967 $fieldset = $DB->get_fieldset_sql("SELECT * FROM {{$tablename}} WHERE course > ?", array(1)); 1968 $this->assertInternalType('array', $fieldset); 1969 1970 $this->assertCount(3, $fieldset); 1971 $this->assertEquals(2, $fieldset[0]); 1972 $this->assertEquals(3, $fieldset[1]); 1973 $this->assertEquals(4, $fieldset[2]); 1974 1975 $fieldset = $DB->get_fieldset_sql("SELECT onebinary FROM {{$tablename}} WHERE course > ?", array(1)); 1976 $this->assertInternalType('array', $fieldset); 1977 1978 $this->assertCount(3, $fieldset); 1979 $this->assertEquals($binarydata, $fieldset[0]); 1980 $this->assertEquals($binarydata, $fieldset[1]); 1981 $this->assertEquals($binarydata, $fieldset[2]); 1982 } 1983 1984 public function test_insert_record_raw() { 1985 $DB = $this->tdb; 1986 $dbman = $DB->get_manager(); 1987 1988 $table = $this->get_test_table(); 1989 $tablename = $table->getName(); 1990 1991 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 1992 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 1993 $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring'); 1994 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 1995 $dbman->create_table($table); 1996 1997 $record = (object)array('course' => 1, 'onechar' => 'xx'); 1998 $before = clone($record); 1999 $result = $DB->insert_record_raw($tablename, $record); 2000 $this->assertSame(1, $result); 2001 $this->assertEquals($record, $before); 2002 2003 $record = $DB->get_record($tablename, array('course' => 1)); 2004 $this->assertInstanceOf('stdClass', $record); 2005 $this->assertSame('xx', $record->onechar); 2006 2007 $result = $DB->insert_record_raw($tablename, array('course' => 2, 'onechar' => 'yy'), false); 2008 $this->assertTrue($result); 2009 2010 // Note: bulk not implemented yet. 2011 $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'zz'), true, true); 2012 $record = $DB->get_record($tablename, array('course' => 3)); 2013 $this->assertInstanceOf('stdClass', $record); 2014 $this->assertSame('zz', $record->onechar); 2015 2016 // Custom sequence (id) - returnid is ignored. 2017 $result = $DB->insert_record_raw($tablename, array('id' => 10, 'course' => 3, 'onechar' => 'bb'), true, false, true); 2018 $this->assertTrue($result); 2019 $record = $DB->get_record($tablename, array('id' => 10)); 2020 $this->assertInstanceOf('stdClass', $record); 2021 $this->assertSame('bb', $record->onechar); 2022 2023 // Custom sequence - missing id error. 2024 try { 2025 $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'bb'), true, false, true); 2026 $this->fail('Exception expected due to missing record'); 2027 } catch (coding_exception $ex) { 2028 $this->assertTrue(true); 2029 } 2030 2031 // Wrong column error. 2032 try { 2033 $DB->insert_record_raw($tablename, array('xxxxx' => 3, 'onechar' => 'bb')); 2034 $this->fail('Exception expected due to invalid column'); 2035 } catch (dml_exception $ex) { 2036 $this->assertTrue(true); 2037 } 2038 2039 // Create something similar to "context_temp" with id column without sequence. 2040 $dbman->drop_table($table); 2041 $table = $this->get_test_table(); 2042 $tablename = $table->getName(); 2043 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); 2044 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 2045 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 2046 $dbman->create_table($table); 2047 2048 $record = (object)array('id'=>5, 'course' => 1); 2049 $DB->insert_record_raw($tablename, $record, false, false, true); 2050 $record = $DB->get_record($tablename, array()); 2051 $this->assertEquals(5, $record->id); 2052 } 2053 2054 public function test_insert_record() { 2055 // All the information in this test is fetched from DB by get_recordset() so we 2056 // have such method properly tested against nulls, empties and friends... 2057 2058 $DB = $this->tdb; 2059 $dbman = $DB->get_manager(); 2060 2061 $table = $this->get_test_table(); 2062 $tablename = $table->getName(); 2063 2064 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 2065 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 2066 $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, null, null, 100); 2067 $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200); 2068 $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring'); 2069 $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null); 2070 $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null); 2071 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 2072 $dbman->create_table($table); 2073 2074 $this->assertSame(1, $DB->insert_record($tablename, array('course' => 1), true)); 2075 $record = $DB->get_record($tablename, array('course' => 1)); 2076 $this->assertEquals(1, $record->id); 2077 $this->assertEquals(100, $record->oneint); // Just check column defaults have been applied. 2078 $this->assertEquals(200, $record->onenum); 2079 $this->assertSame('onestring', $record->onechar); 2080 $this->assertNull($record->onetext); 2081 $this->assertNull($record->onebinary); 2082 2083 // Without returning id, bulk not implemented. 2084 $result = $this->assertTrue($DB->insert_record($tablename, array('course' => 99), false, true)); 2085 $record = $DB->get_record($tablename, array('course' => 99)); 2086 $this->assertEquals(2, $record->id); 2087 $this->assertEquals(99, $record->course); 2088 2089 // Check nulls are set properly for all types. 2090 $record = new stdClass(); 2091 $record->oneint = null; 2092 $record->onenum = null; 2093 $record->onechar = null; 2094 $record->onetext = null; 2095 $record->onebinary = null; 2096 $recid = $DB->insert_record($tablename, $record); 2097 $record = $DB->get_record($tablename, array('id' => $recid)); 2098 $this->assertEquals(0, $record->course); 2099 $this->assertNull($record->oneint); 2100 $this->assertNull($record->onenum); 2101 $this->assertNull($record->onechar); 2102 $this->assertNull($record->onetext); 2103 $this->assertNull($record->onebinary); 2104 2105 // Check zeros are set properly for all types. 2106 $record = new stdClass(); 2107 $record->oneint = 0; 2108 $record->onenum = 0; 2109 $recid = $DB->insert_record($tablename, $record); 2110 $record = $DB->get_record($tablename, array('id' => $recid)); 2111 $this->assertEquals(0, $record->oneint); 2112 $this->assertEquals(0, $record->onenum); 2113 2114 // Check booleans are set properly for all types. 2115 $record = new stdClass(); 2116 $record->oneint = true; // Trues. 2117 $record->onenum = true; 2118 $record->onechar = true; 2119 $record->onetext = true; 2120 $recid = $DB->insert_record($tablename, $record); 2121 $record = $DB->get_record($tablename, array('id' => $recid)); 2122 $this->assertEquals(1, $record->oneint); 2123 $this->assertEquals(1, $record->onenum); 2124 $this->assertEquals(1, $record->onechar); 2125 $this->assertEquals(1, $record->onetext); 2126 2127 $record = new stdClass(); 2128 $record->oneint = false; // Falses. 2129 $record->onenum = false; 2130 $record->onechar = false; 2131 $record->onetext = false; 2132 $recid = $DB->insert_record($tablename, $record); 2133 $record = $DB->get_record($tablename, array('id' => $recid)); 2134 $this->assertEquals(0, $record->oneint); 2135 $this->assertEquals(0, $record->onenum); 2136 $this->assertEquals(0, $record->onechar); 2137 $this->assertEquals(0, $record->onetext); 2138 2139 // Check string data causes exception in numeric types. 2140 $record = new stdClass(); 2141 $record->oneint = 'onestring'; 2142 $record->onenum = 0; 2143 try { 2144 $DB->insert_record($tablename, $record); 2145 $this->fail("Expecting an exception, none occurred"); 2146 } catch (moodle_exception $e) { 2147 $this->assertInstanceOf('dml_exception', $e); 2148 } 2149 $record = new stdClass(); 2150 $record->oneint = 0; 2151 $record->onenum = 'onestring'; 2152 try { 2153 $DB->insert_record($tablename, $record); 2154 $this->fail("Expecting an exception, none occurred"); 2155 } catch (moodle_exception $e) { 2156 $this->assertInstanceOf('dml_exception', $e); 2157 } 2158 2159 // Check empty string data is stored as 0 in numeric datatypes. 2160 $record = new stdClass(); 2161 $record->oneint = ''; // Empty string. 2162 $record->onenum = 0; 2163 $recid = $DB->insert_record($tablename, $record); 2164 $record = $DB->get_record($tablename, array('id' => $recid)); 2165 $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0); 2166 2167 $record = new stdClass(); 2168 $record->oneint = 0; 2169 $record->onenum = ''; // Empty string. 2170 $recid = $DB->insert_record($tablename, $record); 2171 $record = $DB->get_record($tablename, array('id' => $recid)); 2172 $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0); 2173 2174 // Check empty strings are set properly in string types. 2175 $record = new stdClass(); 2176 $record->oneint = 0; 2177 $record->onenum = 0; 2178 $record->onechar = ''; 2179 $record->onetext = ''; 2180 $recid = $DB->insert_record($tablename, $record); 2181 $record = $DB->get_record($tablename, array('id' => $recid)); 2182 $this->assertTrue($record->onechar === ''); 2183 $this->assertTrue($record->onetext === ''); 2184 2185 // Check operation ((210.10 + 39.92) - 150.02) against numeric types. 2186 $record = new stdClass(); 2187 $record->oneint = ((210.10 + 39.92) - 150.02); 2188 $record->onenum = ((210.10 + 39.92) - 150.02); 2189 $recid = $DB->insert_record($tablename, $record); 2190 $record = $DB->get_record($tablename, array('id' => $recid)); 2191 $this->assertEquals(100, $record->oneint); 2192 $this->assertEquals(100, $record->onenum); 2193 2194 // Check various quotes/backslashes combinations in string types. 2195 $teststrings = array( 2196 'backslashes and quotes alone (even): "" \'\' \\\\', 2197 'backslashes and quotes alone (odd): """ \'\'\' \\\\\\', 2198 'backslashes and quotes sequences (even): \\"\\" \\\'\\\'', 2199 'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\''); 2200 foreach ($teststrings as $teststring) { 2201 $record = new stdClass(); 2202 $record->onechar = $teststring; 2203 $record->onetext = $teststring; 2204 $recid = $DB->insert_record($tablename, $record); 2205 $record = $DB->get_record($tablename, array('id' => $recid)); 2206 $this->assertEquals($teststring, $record->onechar); 2207 $this->assertEquals($teststring, $record->onetext); 2208 } 2209 2210 // Check LOBs in text/binary columns. 2211 $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt'); 2212 $blob = file_get_contents(__DIR__ . '/fixtures/randombinary'); 2213 $record = new stdClass(); 2214 $record->onetext = $clob; 2215 $record->onebinary = $blob; 2216 $recid = $DB->insert_record($tablename, $record); 2217 $rs = $DB->get_recordset($tablename, array('id' => $recid)); 2218 $record = $rs->current(); 2219 $rs->close(); 2220 $this->assertEquals($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)'); 2221 $this->assertEquals($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)'); 2222 2223 // And "small" LOBs too, just in case. 2224 $newclob = substr($clob, 0, 500); 2225 $newblob = substr($blob, 0, 250); 2226 $record = new stdClass(); 2227 $record->onetext = $newclob; 2228 $record->onebinary = $newblob; 2229 $recid = $DB->insert_record($tablename, $record); 2230 $rs = $DB->get_recordset($tablename, array('id' => $recid)); 2231 $record = $rs->current(); 2232 $rs->close(); 2233 $this->assertEquals($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)'); 2234 $this->assertEquals($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)'); 2235 $this->assertEquals(false, $rs->key()); // Ensure recordset key() method to be working ok after closing. 2236 2237 // And "diagnostic" LOBs too, just in case. 2238 $newclob = '\'"\\;/ěščřžýáíé'; 2239 $newblob = '\'"\\;/ěščřžýáíé'; 2240 $record = new stdClass(); 2241 $record->onetext = $newclob; 2242 $record->onebinary = $newblob; 2243 $recid = $DB->insert_record($tablename, $record); 2244 $rs = $DB->get_recordset($tablename, array('id' => $recid)); 2245 $record = $rs->current(); 2246 $rs->close(); 2247 $this->assertSame($newclob, $record->onetext); 2248 $this->assertSame($newblob, $record->onebinary); 2249 $this->assertEquals(false, $rs->key()); // Ensure recordset key() method to be working ok after closing. 2250 2251 // Test data is not modified. 2252 $record = new stdClass(); 2253 $record->id = -1; // Has to be ignored. 2254 $record->course = 3; 2255 $record->lalala = 'lalal'; // Unused. 2256 $before = clone($record); 2257 $DB->insert_record($tablename, $record); 2258 $this->assertEquals($record, $before); 2259 2260 // Make sure the id is always increasing and never reuses the same id. 2261 $id1 = $DB->insert_record($tablename, array('course' => 3)); 2262 $id2 = $DB->insert_record($tablename, array('course' => 3)); 2263 $this->assertTrue($id1 < $id2); 2264 $DB->delete_records($tablename, array('id'=>$id2)); 2265 $id3 = $DB->insert_record($tablename, array('course' => 3)); 2266 $this->assertTrue($id2 < $id3); 2267 $DB->delete_records($tablename, array()); 2268 $id4 = $DB->insert_record($tablename, array('course' => 3)); 2269 $this->assertTrue($id3 < $id4); 2270 2271 // Test saving a float in a CHAR column, and reading it back. 2272 $id = $DB->insert_record($tablename, array('onechar' => 1.0)); 2273 $this->assertEquals(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2274 $id = $DB->insert_record($tablename, array('onechar' => 1e20)); 2275 $this->assertEquals(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2276 $id = $DB->insert_record($tablename, array('onechar' => 1e-4)); 2277 $this->assertEquals(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2278 $id = $DB->insert_record($tablename, array('onechar' => 1e-5)); 2279 $this->assertEquals(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2280 $id = $DB->insert_record($tablename, array('onechar' => 1e-300)); 2281 $this->assertEquals(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2282 $id = $DB->insert_record($tablename, array('onechar' => 1e300)); 2283 $this->assertEquals(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2284 2285 // Test saving a float in a TEXT column, and reading it back. 2286 $id = $DB->insert_record($tablename, array('onetext' => 1.0)); 2287 $this->assertEquals(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2288 $id = $DB->insert_record($tablename, array('onetext' => 1e20)); 2289 $this->assertEquals(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2290 $id = $DB->insert_record($tablename, array('onetext' => 1e-4)); 2291 $this->assertEquals(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2292 $id = $DB->insert_record($tablename, array('onetext' => 1e-5)); 2293 $this->assertEquals(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2294 $id = $DB->insert_record($tablename, array('onetext' => 1e-300)); 2295 $this->assertEquals(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2296 $id = $DB->insert_record($tablename, array('onetext' => 1e300)); 2297 $this->assertEquals(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2298 2299 // Test that inserting data violating one unique key leads to error. 2300 // Empty the table completely. 2301 $this->assertTrue($DB->delete_records($tablename)); 2302 2303 // Add one unique constraint (index). 2304 $key = new xmldb_key('testuk', XMLDB_KEY_UNIQUE, array('course', 'oneint')); 2305 $dbman->add_key($table, $key); 2306 2307 // Let's insert one record violating the constraint multiple times. 2308 $record = (object)array('course' => 1, 'oneint' => 1); 2309 $this->assertTrue($DB->insert_record($tablename, $record, false)); // Insert 1st. No problem expected. 2310 2311 // Re-insert same record, not returning id. dml_exception expected. 2312 try { 2313 $DB->insert_record($tablename, $record, false); 2314 $this->fail("Expecting an exception, none occurred"); 2315 } catch (moodle_exception $e) { 2316 $this->assertInstanceOf('dml_exception', $e); 2317 } 2318 2319 // Re-insert same record, returning id. dml_exception expected. 2320 try { 2321 $DB->insert_record($tablename, $record, true); 2322 $this->fail("Expecting an exception, none occurred"); 2323 } catch (moodle_exception $e) { 2324 $this->assertInstanceOf('dml_exception', $e); 2325 } 2326 2327 // Try to insert a record into a non-existent table. dml_exception expected. 2328 try { 2329 $DB->insert_record('nonexistenttable', $record, true); 2330 $this->fail("Expecting an exception, none occurred"); 2331 } catch (exception $e) { 2332 $this->assertTrue($e instanceof dml_exception); 2333 } 2334 } 2335 2336 public function test_insert_records() { 2337 $DB = $this->tdb; 2338 $dbman = $DB->get_manager(); 2339 2340 $table = $this->get_test_table(); 2341 $tablename = $table->getName(); 2342 2343 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 2344 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 2345 $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, null, null, 100); 2346 $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200); 2347 $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring'); 2348 $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null); 2349 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 2350 $dbman->create_table($table); 2351 2352 $this->assertCount(0, $DB->get_records($tablename)); 2353 2354 $record = new stdClass(); 2355 $record->id = '1'; 2356 $record->course = '1'; 2357 $record->oneint = null; 2358 $record->onenum = 1.0; 2359 $record->onechar = 'a'; 2360 $record->onetext = 'aaa'; 2361 2362 $expected = array(); 2363 $records = array(); 2364 for ($i = 1; $i <= 2000; $i++) { // This may take a while, it should be higher than defaults in DML drivers. 2365 $rec = clone($record); 2366 $rec->id = (string)$i; 2367 $rec->oneint = (string)$i; 2368 $expected[$i] = $rec; 2369 $rec = clone($rec); 2370 unset($rec->id); 2371 $records[$i] = $rec; 2372 } 2373 2374 $DB->insert_records($tablename, $records); 2375 $stored = $DB->get_records($tablename, array(), 'id ASC'); 2376 $this->assertEquals($expected, $stored); 2377 2378 // Test there can be some extra properties including id. 2379 $count = $DB->count_records($tablename); 2380 $rec1 = (array)$record; 2381 $rec1['xxx'] = 1; 2382 $rec2 = (array)$record; 2383 $rec2['xxx'] = 2; 2384 2385 $records = array($rec1, $rec2); 2386 $DB->insert_records($tablename, $records); 2387 $this->assertEquals($count + 2, $DB->count_records($tablename)); 2388 2389 // Test not all properties are necessary. 2390 $rec1 = (array)$record; 2391 unset($rec1['course']); 2392 $rec2 = (array)$record; 2393 unset($rec2['course']); 2394 2395 $records = array($rec1, $rec2); 2396 $DB->insert_records($tablename, $records); 2397 2398 // Make sure no changes in data object structure are tolerated. 2399 $rec1 = (array)$record; 2400 unset($rec1['id']); 2401 $rec2 = (array)$record; 2402 unset($rec2['id']); 2403 2404 $records = array($rec1, $rec2); 2405 $DB->insert_records($tablename, $records); 2406 2407 $rec2['xx'] = '1'; 2408 $records = array($rec1, $rec2); 2409 try { 2410 $DB->insert_records($tablename, $records); 2411 $this->fail('coding_exception expected when insert_records receives different object data structures'); 2412 } catch (moodle_exception $e) { 2413 $this->assertInstanceOf('coding_exception', $e); 2414 } 2415 2416 unset($rec2['xx']); 2417 unset($rec2['course']); 2418 $rec2['course'] = '1'; 2419 $records = array($rec1, $rec2); 2420 try { 2421 $DB->insert_records($tablename, $records); 2422 $this->fail('coding_exception expected when insert_records receives different object data structures'); 2423 } catch (moodle_exception $e) { 2424 $this->assertInstanceOf('coding_exception', $e); 2425 } 2426 2427 $records = 1; 2428 try { 2429 $DB->insert_records($tablename, $records); 2430 $this->fail('coding_exception expected when insert_records receives non-traversable data'); 2431 } catch (moodle_exception $e) { 2432 $this->assertInstanceOf('coding_exception', $e); 2433 } 2434 2435 $records = array(1); 2436 try { 2437 $DB->insert_records($tablename, $records); 2438 $this->fail('coding_exception expected when insert_records receives non-objet record'); 2439 } catch (moodle_exception $e) { 2440 $this->assertInstanceOf('coding_exception', $e); 2441 } 2442 } 2443 2444 public function test_insert_record_with_nullable_unique_index() { 2445 $DB = $this->tdb; 2446 $dbman = $DB->get_manager(); 2447 2448 $table = $this->get_test_table(); 2449 $tablename = $table->getName(); 2450 2451 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 2452 $table->add_field('notnull1', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 2453 $table->add_field('nullable1', XMLDB_TYPE_INTEGER, '10', null, null, null, null); 2454 $table->add_field('nullable2', XMLDB_TYPE_INTEGER, '10', null, null, null, null); 2455 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 2456 $table->add_index('notnull1-nullable1-nullable2', XMLDB_INDEX_UNIQUE, 2457 array('notnull1', 'nullable1', 'nullable2')); 2458 $dbman->create_table($table); 2459 2460 // Insert one record. Should be OK (no exception). 2461 $DB->insert_record($tablename, (object) ['notnull1' => 1, 'nullable1' => 1, 'nullable2' => 1]); 2462 2463 $this->assertEquals(1, $DB->count_records($table->getName())); 2464 $this->assertEquals(1, $DB->count_records($table->getName(), ['nullable1' => 1])); 2465 2466 // Inserting a duplicate should fail. 2467 try { 2468 $DB->insert_record($tablename, (object) ['notnull1' => 1, 'nullable1' => 1, 'nullable2' => 1]); 2469 $this->fail('dml_write_exception expected when a record violates a unique index'); 2470 } catch (moodle_exception $e) { 2471 $this->assertInstanceOf('dml_write_exception', $e); 2472 } 2473 2474 $this->assertEquals(1, $DB->count_records($table->getName())); 2475 $this->assertEquals(1, $DB->count_records($table->getName(), ['nullable1' => 1])); 2476 2477 // Inserting a record with nulls in the nullable columns should work. 2478 $DB->insert_record($tablename, (object) ['notnull1' => 1, 'nullable1' => null, 'nullable2' => null]); 2479 2480 $this->assertEquals(2, $DB->count_records($table->getName())); 2481 $this->assertEquals(1, $DB->count_records($table->getName(), ['nullable1' => 1])); 2482 $this->assertEquals(1, $DB->count_records($table->getName(), ['nullable1' => null])); 2483 2484 // And it should be possible to insert a duplicate. 2485 $DB->insert_record($tablename, (object) ['notnull1' => 1, 'nullable1' => null, 'nullable2' => null]); 2486 2487 $this->assertEquals(3, $DB->count_records($table->getName())); 2488 $this->assertEquals(1, $DB->count_records($table->getName(), ['nullable1' => 1])); 2489 $this->assertEquals(2, $DB->count_records($table->getName(), ['nullable1' => null])); 2490 2491 // Same, but with only one of the nullable columns being null. 2492 $DB->insert_record($tablename, (object) ['notnull1' => 1, 'nullable1' => 1, 'nullable2' => null]); 2493 2494 $this->assertEquals(4, $DB->count_records($table->getName())); 2495 $this->assertEquals(2, $DB->count_records($table->getName(), ['nullable1' => 1])); 2496 $this->assertEquals(2, $DB->count_records($table->getName(), ['nullable1' => null])); 2497 2498 $DB->insert_record($tablename, (object) ['notnull1' => 1, 'nullable1' => 1, 'nullable2' => null]); 2499 2500 $this->assertEquals(5, $DB->count_records($table->getName())); 2501 $this->assertEquals(3, $DB->count_records($table->getName(), ['nullable1' => 1])); 2502 $this->assertEquals(2, $DB->count_records($table->getName(), ['nullable1' => null])); 2503 2504 } 2505 2506 public function test_import_record() { 2507 // All the information in this test is fetched from DB by get_recordset() so we 2508 // have such method properly tested against nulls, empties and friends... 2509 2510 $DB = $this->tdb; 2511 $dbman = $DB->get_manager(); 2512 2513 $table = $this->get_test_table(); 2514 $tablename = $table->getName(); 2515 2516 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 2517 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 2518 $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, null, null, 100); 2519 $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200); 2520 $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring'); 2521 $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null); 2522 $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null); 2523 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 2524 $dbman->create_table($table); 2525 2526 $this->assertSame(1, $DB->insert_record($tablename, array('course' => 1), true)); 2527 $record = $DB->get_record($tablename, array('course' => 1)); 2528 $this->assertEquals(1, $record->id); 2529 $this->assertEquals(100, $record->oneint); // Just check column defaults have been applied. 2530 $this->assertEquals(200, $record->onenum); 2531 $this->assertSame('onestring', $record->onechar); 2532 $this->assertNull($record->onetext); 2533 $this->assertNull($record->onebinary); 2534 2535 // Ignore extra columns. 2536 $record = (object)array('id'=>13, 'course'=>2, 'xxxx'=>788778); 2537 $before = clone($record); 2538 $this->assertTrue($DB->import_record($tablename, $record)); 2539 $this->assertEquals($record, $before); 2540 $records = $DB->get_records($tablename); 2541 $this->assertEquals(2, $records[13]->course); 2542 2543 // Check nulls are set properly for all types. 2544 $record = new stdClass(); 2545 $record->id = 20; 2546 $record->oneint = null; 2547 $record->onenum = null; 2548 $record->onechar = null; 2549 $record->onetext = null; 2550 $record->onebinary = null; 2551 $this->assertTrue($DB->import_record($tablename, $record)); 2552 $record = $DB->get_record($tablename, array('id' => 20)); 2553 $this->assertEquals(0, $record->course); 2554 $this->assertNull($record->oneint); 2555 $this->assertNull($record->onenum); 2556 $this->assertNull($record->onechar); 2557 $this->assertNull($record->onetext); 2558 $this->assertNull($record->onebinary); 2559 2560 // Check zeros are set properly for all types. 2561 $record = new stdClass(); 2562 $record->id = 23; 2563 $record->oneint = 0; 2564 $record->onenum = 0; 2565 $this->assertTrue($DB->import_record($tablename, $record)); 2566 $record = $DB->get_record($tablename, array('id' => 23)); 2567 $this->assertEquals(0, $record->oneint); 2568 $this->assertEquals(0, $record->onenum); 2569 2570 // Check string data causes exception in numeric types. 2571 $record = new stdClass(); 2572 $record->id = 32; 2573 $record->oneint = 'onestring'; 2574 $record->onenum = 0; 2575 try { 2576 $DB->import_record($tablename, $record); 2577 $this->fail("Expecting an exception, none occurred"); 2578 } catch (moodle_exception $e) { 2579 $this->assertInstanceOf('dml_exception', $e); 2580 } 2581 $record = new stdClass(); 2582 $record->id = 35; 2583 $record->oneint = 0; 2584 $record->onenum = 'onestring'; 2585 try { 2586 $DB->import_record($tablename, $record); 2587 $this->fail("Expecting an exception, none occurred"); 2588 } catch (moodle_exception $e) { 2589 $this->assertInstanceOf('dml_exception', $e); 2590 } 2591 2592 // Check empty strings are set properly in string types. 2593 $record = new stdClass(); 2594 $record->id = 44; 2595 $record->oneint = 0; 2596 $record->onenum = 0; 2597 $record->onechar = ''; 2598 $record->onetext = ''; 2599 $this->assertTrue($DB->import_record($tablename, $record)); 2600 $record = $DB->get_record($tablename, array('id' => 44)); 2601 $this->assertTrue($record->onechar === ''); 2602 $this->assertTrue($record->onetext === ''); 2603 2604 // Check operation ((210.10 + 39.92) - 150.02) against numeric types. 2605 $record = new stdClass(); 2606 $record->id = 47; 2607 $record->oneint = ((210.10 + 39.92) - 150.02); 2608 $record->onenum = ((210.10 + 39.92) - 150.02); 2609 $this->assertTrue($DB->import_record($tablename, $record)); 2610 $record = $DB->get_record($tablename, array('id' => 47)); 2611 $this->assertEquals(100, $record->oneint); 2612 $this->assertEquals(100, $record->onenum); 2613 2614 // Check various quotes/backslashes combinations in string types. 2615 $i = 50; 2616 $teststrings = array( 2617 'backslashes and quotes alone (even): "" \'\' \\\\', 2618 'backslashes and quotes alone (odd): """ \'\'\' \\\\\\', 2619 'backslashes and quotes sequences (even): \\"\\" \\\'\\\'', 2620 'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\''); 2621 foreach ($teststrings as $teststring) { 2622 $record = new stdClass(); 2623 $record->id = $i; 2624 $record->onechar = $teststring; 2625 $record->onetext = $teststring; 2626 $this->assertTrue($DB->import_record($tablename, $record)); 2627 $record = $DB->get_record($tablename, array('id' => $i)); 2628 $this->assertEquals($teststring, $record->onechar); 2629 $this->assertEquals($teststring, $record->onetext); 2630 $i = $i + 3; 2631 } 2632 2633 // Check LOBs in text/binary columns. 2634 $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt'); 2635 $record = new stdClass(); 2636 $record->id = 70; 2637 $record->onetext = $clob; 2638 $record->onebinary = ''; 2639 $this->assertTrue($DB->import_record($tablename, $record)); 2640 $rs = $DB->get_recordset($tablename, array('id' => 70)); 2641 $record = $rs->current(); 2642 $rs->close(); 2643 $this->assertEquals($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)'); 2644 2645 $blob = file_get_contents(__DIR__ . '/fixtures/randombinary'); 2646 $record = new stdClass(); 2647 $record->id = 71; 2648 $record->onetext = ''; 2649 $record->onebinary = $blob; 2650 $this->assertTrue($DB->import_record($tablename, $record)); 2651 $rs = $DB->get_recordset($tablename, array('id' => 71)); 2652 $record = $rs->current(); 2653 $rs->close(); 2654 $this->assertEquals($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)'); 2655 2656 // And "small" LOBs too, just in case. 2657 $newclob = substr($clob, 0, 500); 2658 $newblob = substr($blob, 0, 250); 2659 $record = new stdClass(); 2660 $record->id = 73; 2661 $record->onetext = $newclob; 2662 $record->onebinary = $newblob; 2663 $this->assertTrue($DB->import_record($tablename, $record)); 2664 $rs = $DB->get_recordset($tablename, array('id' => 73)); 2665 $record = $rs->current(); 2666 $rs->close(); 2667 $this->assertEquals($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)'); 2668 $this->assertEquals($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)'); 2669 $this->assertEquals(false, $rs->key()); // Ensure recordset key() method to be working ok after closing. 2670 } 2671 2672 public function test_update_record_raw() { 2673 $DB = $this->tdb; 2674 $dbman = $DB->get_manager(); 2675 2676 $table = $this->get_test_table(); 2677 $tablename = $table->getName(); 2678 2679 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 2680 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 2681 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 2682 $dbman->create_table($table); 2683 2684 $DB->insert_record($tablename, array('course' => 1)); 2685 $DB->insert_record($tablename, array('course' => 3)); 2686 2687 $record = $DB->get_record($tablename, array('course' => 1)); 2688 $record->course = 2; 2689 $this->assertTrue($DB->update_record_raw($tablename, $record)); 2690 $this->assertEquals(0, $DB->count_records($tablename, array('course' => 1))); 2691 $this->assertEquals(1, $DB->count_records($tablename, array('course' => 2))); 2692 $this->assertEquals(1, $DB->count_records($tablename, array('course' => 3))); 2693 2694 $record = $DB->get_record($tablename, array('course' => 3)); 2695 $record->xxxxx = 2; 2696 try { 2697 $DB->update_record_raw($tablename, $record); 2698 $this->fail("Expecting an exception, none occurred"); 2699 } catch (moodle_exception $e) { 2700 $this->assertInstanceOf('moodle_exception', $e); 2701 } 2702 2703 $record = $DB->get_record($tablename, array('course' => 3)); 2704 unset($record->id); 2705 try { 2706 $DB->update_record_raw($tablename, $record); 2707 $this->fail("Expecting an exception, none occurred"); 2708 } catch (moodle_exception $e) { 2709 $this->assertInstanceOf('coding_exception', $e); 2710 } 2711 } 2712 2713 public function test_update_record() { 2714 2715 // All the information in this test is fetched from DB by get_record() so we 2716 // have such method properly tested against nulls, empties and friends... 2717 2718 $DB = $this->tdb; 2719 $dbman = $DB->get_manager(); 2720 2721 $table = $this->get_test_table(); 2722 $tablename = $table->getName(); 2723 2724 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 2725 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 2726 $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, null, null, 100); 2727 $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200); 2728 $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring'); 2729 $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null); 2730 $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null); 2731 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 2732 $dbman->create_table($table); 2733 2734 $DB->insert_record($tablename, array('course' => 1)); 2735 $record = $DB->get_record($tablename, array('course' => 1)); 2736 $record->course = 2; 2737 2738 $this->assertTrue($DB->update_record($tablename, $record)); 2739 $this->assertFalse($record = $DB->get_record($tablename, array('course' => 1))); 2740 $this->assertNotEmpty($record = $DB->get_record($tablename, array('course' => 2))); 2741 $this->assertEquals(100, $record->oneint); // Just check column defaults have been applied. 2742 $this->assertEquals(200, $record->onenum); 2743 $this->assertSame('onestring', $record->onechar); 2744 $this->assertNull($record->onetext); 2745 $this->assertNull($record->onebinary); 2746 2747 // Check nulls are set properly for all types. 2748 $record->oneint = null; 2749 $record->onenum = null; 2750 $record->onechar = null; 2751 $record->onetext = null; 2752 $record->onebinary = null; 2753 $DB->update_record($tablename, $record); 2754 $record = $DB->get_record($tablename, array('course' => 2)); 2755 $this->assertNull($record->oneint); 2756 $this->assertNull($record->onenum); 2757 $this->assertNull($record->onechar); 2758 $this->assertNull($record->onetext); 2759 $this->assertNull($record->onebinary); 2760 2761 // Check zeros are set properly for all types. 2762 $record->oneint = 0; 2763 $record->onenum = 0; 2764 $DB->update_record($tablename, $record); 2765 $record = $DB->get_record($tablename, array('course' => 2)); 2766 $this->assertEquals(0, $record->oneint); 2767 $this->assertEquals(0, $record->onenum); 2768 2769 // Check booleans are set properly for all types. 2770 $record->oneint = true; // Trues. 2771 $record->onenum = true; 2772 $record->onechar = true; 2773 $record->onetext = true; 2774 $DB->update_record($tablename, $record); 2775 $record = $DB->get_record($tablename, array('course' => 2)); 2776 $this->assertEquals(1, $record->oneint); 2777 $this->assertEquals(1, $record->onenum); 2778 $this->assertEquals(1, $record->onechar); 2779 $this->assertEquals(1, $record->onetext); 2780 2781 $record->oneint = false; // Falses. 2782 $record->onenum = false; 2783 $record->onechar = false; 2784 $record->onetext = false; 2785 $DB->update_record($tablename, $record); 2786 $record = $DB->get_record($tablename, array('course' => 2)); 2787 $this->assertEquals(0, $record->oneint); 2788 $this->assertEquals(0, $record->onenum); 2789 $this->assertEquals(0, $record->onechar); 2790 $this->assertEquals(0, $record->onetext); 2791 2792 // Check string data causes exception in numeric types. 2793 $record->oneint = 'onestring'; 2794 $record->onenum = 0; 2795 try { 2796 $DB->update_record($tablename, $record); 2797 $this->fail("Expecting an exception, none occurred"); 2798 } catch (moodle_exception $e) { 2799 $this->assertInstanceOf('dml_exception', $e); 2800 } 2801 $record->oneint = 0; 2802 $record->onenum = 'onestring'; 2803 try { 2804 $DB->update_record($tablename, $record); 2805 $this->fail("Expecting an exception, none occurred"); 2806 } catch (moodle_exception $e) { 2807 $this->assertInstanceOf('dml_exception', $e); 2808 } 2809 2810 // Check empty string data is stored as 0 in numeric datatypes. 2811 $record->oneint = ''; // Empty string. 2812 $record->onenum = 0; 2813 $DB->update_record($tablename, $record); 2814 $record = $DB->get_record($tablename, array('course' => 2)); 2815 $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0); 2816 2817 $record->oneint = 0; 2818 $record->onenum = ''; // Empty string. 2819 $DB->update_record($tablename, $record); 2820 $record = $DB->get_record($tablename, array('course' => 2)); 2821 $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0); 2822 2823 // Check empty strings are set properly in string types. 2824 $record->oneint = 0; 2825 $record->onenum = 0; 2826 $record->onechar = ''; 2827 $record->onetext = ''; 2828 $DB->update_record($tablename, $record); 2829 $record = $DB->get_record($tablename, array('course' => 2)); 2830 $this->assertTrue($record->onechar === ''); 2831 $this->assertTrue($record->onetext === ''); 2832 2833 // Check operation ((210.10 + 39.92) - 150.02) against numeric types. 2834 $record->oneint = ((210.10 + 39.92) - 150.02); 2835 $record->onenum = ((210.10 + 39.92) - 150.02); 2836 $DB->update_record($tablename, $record); 2837 $record = $DB->get_record($tablename, array('course' => 2)); 2838 $this->assertEquals(100, $record->oneint); 2839 $this->assertEquals(100, $record->onenum); 2840 2841 // Check various quotes/backslashes combinations in string types. 2842 $teststrings = array( 2843 'backslashes and quotes alone (even): "" \'\' \\\\', 2844 'backslashes and quotes alone (odd): """ \'\'\' \\\\\\', 2845 'backslashes and quotes sequences (even): \\"\\" \\\'\\\'', 2846 'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\''); 2847 foreach ($teststrings as $teststring) { 2848 $record->onechar = $teststring; 2849 $record->onetext = $teststring; 2850 $DB->update_record($tablename, $record); 2851 $record = $DB->get_record($tablename, array('course' => 2)); 2852 $this->assertEquals($teststring, $record->onechar); 2853 $this->assertEquals($teststring, $record->onetext); 2854 } 2855 2856 // Check LOBs in text/binary columns. 2857 $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt'); 2858 $blob = file_get_contents(__DIR__ . '/fixtures/randombinary'); 2859 $record->onetext = $clob; 2860 $record->onebinary = $blob; 2861 $DB->update_record($tablename, $record); 2862 $record = $DB->get_record($tablename, array('course' => 2)); 2863 $this->assertEquals($clob, $record->onetext, 'Test CLOB update (full contents output disabled)'); 2864 $this->assertEquals($blob, $record->onebinary, 'Test BLOB update (full contents output disabled)'); 2865 2866 // And "small" LOBs too, just in case. 2867 $newclob = substr($clob, 0, 500); 2868 $newblob = substr($blob, 0, 250); 2869 $record->onetext = $newclob; 2870 $record->onebinary = $newblob; 2871 $DB->update_record($tablename, $record); 2872 $record = $DB->get_record($tablename, array('course' => 2)); 2873 $this->assertEquals($newclob, $record->onetext, 'Test "small" CLOB update (full contents output disabled)'); 2874 $this->assertEquals($newblob, $record->onebinary, 'Test "small" BLOB update (full contents output disabled)'); 2875 2876 // Test saving a float in a CHAR column, and reading it back. 2877 $id = $DB->insert_record($tablename, array('onechar' => 'X')); 2878 $DB->update_record($tablename, array('id' => $id, 'onechar' => 1.0)); 2879 $this->assertEquals(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2880 $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e20)); 2881 $this->assertEquals(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2882 $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-4)); 2883 $this->assertEquals(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2884 $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-5)); 2885 $this->assertEquals(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2886 $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-300)); 2887 $this->assertEquals(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2888 $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e300)); 2889 $this->assertEquals(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2890 2891 // Test saving a float in a TEXT column, and reading it back. 2892 $id = $DB->insert_record($tablename, array('onetext' => 'X')); 2893 $DB->update_record($tablename, array('id' => $id, 'onetext' => 1.0)); 2894 $this->assertEquals(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2895 $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e20)); 2896 $this->assertEquals(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2897 $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-4)); 2898 $this->assertEquals(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2899 $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-5)); 2900 $this->assertEquals(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2901 $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-300)); 2902 $this->assertEquals(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2903 $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e300)); 2904 $this->assertEquals(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2905 } 2906 2907 public function test_set_field() { 2908 $DB = $this->tdb; 2909 $dbman = $DB->get_manager(); 2910 2911 $table = $this->get_test_table(); 2912 $tablename = $table->getName(); 2913 2914 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 2915 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 2916 $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null); 2917 $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null); 2918 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 2919 $dbman->create_table($table); 2920 2921 // Simple set_field. 2922 $id1 = $DB->insert_record($tablename, array('course' => 1)); 2923 $id2 = $DB->insert_record($tablename, array('course' => 1)); 2924 $id3 = $DB->insert_record($tablename, array('course' => 3)); 2925 $this->assertTrue($DB->set_field($tablename, 'course', 2, array('id' => $id1))); 2926 $this->assertEquals(2, $DB->get_field($tablename, 'course', array('id' => $id1))); 2927 $this->assertEquals(1, $DB->get_field($tablename, 'course', array('id' => $id2))); 2928 $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id3))); 2929 $DB->delete_records($tablename, array()); 2930 2931 // Multiple fields affected. 2932 $id1 = $DB->insert_record($tablename, array('course' => 1)); 2933 $id2 = $DB->insert_record($tablename, array('course' => 1)); 2934 $id3 = $DB->insert_record($tablename, array('course' => 3)); 2935 $DB->set_field($tablename, 'course', '5', array('course' => 1)); 2936 $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id1))); 2937 $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id2))); 2938 $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id3))); 2939 $DB->delete_records($tablename, array()); 2940 2941 // No field affected. 2942 $id1 = $DB->insert_record($tablename, array('course' => 1)); 2943 $id2 = $DB->insert_record($tablename, array('course' => 1)); 2944 $id3 = $DB->insert_record($tablename, array('course' => 3)); 2945 $DB->set_field($tablename, 'course', '5', array('course' => 0)); 2946 $this->assertEquals(1, $DB->get_field($tablename, 'course', array('id' => $id1))); 2947 $this->assertEquals(1, $DB->get_field($tablename, 'course', array('id' => $id2))); 2948 $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id3))); 2949 $DB->delete_records($tablename, array()); 2950 2951 // All fields - no condition. 2952 $id1 = $DB->insert_record($tablename, array('course' => 1)); 2953 $id2 = $DB->insert_record($tablename, array('course' => 1)); 2954 $id3 = $DB->insert_record($tablename, array('course' => 3)); 2955 $DB->set_field($tablename, 'course', 5, array()); 2956 $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id1))); 2957 $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id2))); 2958 $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id3))); 2959 2960 // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int). 2961 $conditions = array('onetext' => '1'); 2962 try { 2963 $DB->set_field($tablename, 'onechar', 'frog', $conditions); 2964 if (debugging()) { 2965 // Only in debug mode - hopefully all devs test code in debug mode... 2966 $this->fail('An Exception is missing, expected due to equating of text fields'); 2967 } 2968 } catch (moodle_exception $e) { 2969 $this->assertInstanceOf('dml_exception', $e); 2970 $this->assertSame('textconditionsnotallowed', $e->errorcode); 2971 } 2972 2973 // Test saving a float in a CHAR column, and reading it back. 2974 $id = $DB->insert_record($tablename, array('onechar' => 'X')); 2975 $DB->set_field($tablename, 'onechar', 1.0, array('id' => $id)); 2976 $this->assertEquals(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2977 $DB->set_field($tablename, 'onechar', 1e20, array('id' => $id)); 2978 $this->assertEquals(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2979 $DB->set_field($tablename, 'onechar', 1e-4, array('id' => $id)); 2980 $this->assertEquals(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2981 $DB->set_field($tablename, 'onechar', 1e-5, array('id' => $id)); 2982 $this->assertEquals(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2983 $DB->set_field($tablename, 'onechar', 1e-300, array('id' => $id)); 2984 $this->assertEquals(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2985 $DB->set_field($tablename, 'onechar', 1e300, array('id' => $id)); 2986 $this->assertEquals(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id))); 2987 2988 // Test saving a float in a TEXT column, and reading it back. 2989 $id = $DB->insert_record($tablename, array('onetext' => 'X')); 2990 $DB->set_field($tablename, 'onetext', 1.0, array('id' => $id)); 2991 $this->assertEquals(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2992 $DB->set_field($tablename, 'onetext', 1e20, array('id' => $id)); 2993 $this->assertEquals(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2994 $DB->set_field($tablename, 'onetext', 1e-4, array('id' => $id)); 2995 $this->assertEquals(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2996 $DB->set_field($tablename, 'onetext', 1e-5, array('id' => $id)); 2997 $this->assertEquals(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id))); 2998 $DB->set_field($tablename, 'onetext', 1e-300, array('id' => $id)); 2999 $this->assertEquals(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id))); 3000 $DB->set_field($tablename, 'onetext', 1e300, array('id' => $id)); 3001 $this->assertEquals(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id))); 3002 3003 // Note: All the nulls, booleans, empties, quoted and backslashes tests 3004 // go to set_field_select() because set_field() is just one wrapper over it. 3005 } 3006 3007 public function test_set_field_select() { 3008 3009 // All the information in this test is fetched from DB by get_field() so we 3010 // have such method properly tested against nulls, empties and friends... 3011 3012 $DB = $this->tdb; 3013 $dbman = $DB->get_manager(); 3014 3015 $table = $this->get_test_table(); 3016 $tablename = $table->getName(); 3017 3018 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3019 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3020 $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, null, null); 3021 $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null); 3022 $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null); 3023 $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null); 3024 $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null); 3025 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3026 $dbman->create_table($table); 3027 3028 $DB->insert_record($tablename, array('course' => 1)); 3029 3030 $this->assertTrue($DB->set_field_select($tablename, 'course', 2, 'id = ?', array(1))); 3031 $this->assertEquals(2, $DB->get_field($tablename, 'course', array('id' => 1))); 3032 3033 // Check nulls are set properly for all types. 3034 $DB->set_field_select($tablename, 'oneint', null, 'id = ?', array(1)); // Trues. 3035 $DB->set_field_select($tablename, 'onenum', null, 'id = ?', array(1)); 3036 $DB->set_field_select($tablename, 'onechar', null, 'id = ?', array(1)); 3037 $DB->set_field_select($tablename, 'onetext', null, 'id = ?', array(1)); 3038 $DB->set_field_select($tablename, 'onebinary', null, 'id = ?', array(1)); 3039 $this->assertNull($DB->get_field($tablename, 'oneint', array('id' => 1))); 3040 $this->assertNull($DB->get_field($tablename, 'onenum', array('id' => 1))); 3041 $this->assertNull($DB->get_field($tablename, 'onechar', array('id' => 1))); 3042 $this->assertNull($DB->get_field($tablename, 'onetext', array('id' => 1))); 3043 $this->assertNull($DB->get_field($tablename, 'onebinary', array('id' => 1))); 3044 3045 // Check zeros are set properly for all types. 3046 $DB->set_field_select($tablename, 'oneint', 0, 'id = ?', array(1)); 3047 $DB->set_field_select($tablename, 'onenum', 0, 'id = ?', array(1)); 3048 $this->assertEquals(0, $DB->get_field($tablename, 'oneint', array('id' => 1))); 3049 $this->assertEquals(0, $DB->get_field($tablename, 'onenum', array('id' => 1))); 3050 3051 // Check booleans are set properly for all types. 3052 $DB->set_field_select($tablename, 'oneint', true, 'id = ?', array(1)); // Trues. 3053 $DB->set_field_select($tablename, 'onenum', true, 'id = ?', array(1)); 3054 $DB->set_field_select($tablename, 'onechar', true, 'id = ?', array(1)); 3055 $DB->set_field_select($tablename, 'onetext', true, 'id = ?', array(1)); 3056 $this->assertEquals(1, $DB->get_field($tablename, 'oneint', array('id' => 1))); 3057 $this->assertEquals(1, $DB->get_field($tablename, 'onenum', array('id' => 1))); 3058 $this->assertEquals(1, $DB->get_field($tablename, 'onechar', array('id' => 1))); 3059 $this->assertEquals(1, $DB->get_field($tablename, 'onetext', array('id' => 1))); 3060 3061 $DB->set_field_select($tablename, 'oneint', false, 'id = ?', array(1)); // Falses. 3062 $DB->set_field_select($tablename, 'onenum', false, 'id = ?', array(1)); 3063 $DB->set_field_select($tablename, 'onechar', false, 'id = ?', array(1)); 3064 $DB->set_field_select($tablename, 'onetext', false, 'id = ?', array(1)); 3065 $this->assertEquals(0, $DB->get_field($tablename, 'oneint', array('id' => 1))); 3066 $this->assertEquals(0, $DB->get_field($tablename, 'onenum', array('id' => 1))); 3067 $this->assertEquals(0, $DB->get_field($tablename, 'onechar', array('id' => 1))); 3068 $this->assertEquals(0, $DB->get_field($tablename, 'onetext', array('id' => 1))); 3069 3070 // Check string data causes exception in numeric types. 3071 try { 3072 $DB->set_field_select($tablename, 'oneint', 'onestring', 'id = ?', array(1)); 3073 $this->fail("Expecting an exception, none occurred"); 3074 } catch (moodle_exception $e) { 3075 $this->assertInstanceOf('dml_exception', $e); 3076 } 3077 try { 3078 $DB->set_field_select($tablename, 'onenum', 'onestring', 'id = ?', array(1)); 3079 $this->fail("Expecting an exception, none occurred"); 3080 } catch (moodle_exception $e) { 3081 $this->assertInstanceOf('dml_exception', $e); 3082 } 3083 3084 // Check empty string data is stored as 0 in numeric datatypes. 3085 $DB->set_field_select($tablename, 'oneint', '', 'id = ?', array(1)); 3086 $field = $DB->get_field($tablename, 'oneint', array('id' => 1)); 3087 $this->assertTrue(is_numeric($field) && $field == 0); 3088 3089 $DB->set_field_select($tablename, 'onenum', '', 'id = ?', array(1)); 3090 $field = $DB->get_field($tablename, 'onenum', array('id' => 1)); 3091 $this->assertTrue(is_numeric($field) && $field == 0); 3092 3093 // Check empty strings are set properly in string types. 3094 $DB->set_field_select($tablename, 'onechar', '', 'id = ?', array(1)); 3095 $DB->set_field_select($tablename, 'onetext', '', 'id = ?', array(1)); 3096 $this->assertTrue($DB->get_field($tablename, 'onechar', array('id' => 1)) === ''); 3097 $this->assertTrue($DB->get_field($tablename, 'onetext', array('id' => 1)) === ''); 3098 3099 // Check operation ((210.10 + 39.92) - 150.02) against numeric types. 3100 $DB->set_field_select($tablename, 'oneint', ((210.10 + 39.92) - 150.02), 'id = ?', array(1)); 3101 $DB->set_field_select($tablename, 'onenum', ((210.10 + 39.92) - 150.02), 'id = ?', array(1)); 3102 $this->assertEquals(100, $DB->get_field($tablename, 'oneint', array('id' => 1))); 3103 $this->assertEquals(100, $DB->get_field($tablename, 'onenum', array('id' => 1))); 3104 3105 // Check various quotes/backslashes combinations in string types. 3106 $teststrings = array( 3107 'backslashes and quotes alone (even): "" \'\' \\\\', 3108 'backslashes and quotes alone (odd): """ \'\'\' \\\\\\', 3109 'backslashes and quotes sequences (even): \\"\\" \\\'\\\'', 3110 'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\''); 3111 foreach ($teststrings as $teststring) { 3112 $DB->set_field_select($tablename, 'onechar', $teststring, 'id = ?', array(1)); 3113 $DB->set_field_select($tablename, 'onetext', $teststring, 'id = ?', array(1)); 3114 $this->assertEquals($teststring, $DB->get_field($tablename, 'onechar', array('id' => 1))); 3115 $this->assertEquals($teststring, $DB->get_field($tablename, 'onetext', array('id' => 1))); 3116 } 3117 3118 // Check LOBs in text/binary columns. 3119 $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt'); 3120 $blob = file_get_contents(__DIR__ . '/fixtures/randombinary'); 3121 $DB->set_field_select($tablename, 'onetext', $clob, 'id = ?', array(1)); 3122 $DB->set_field_select($tablename, 'onebinary', $blob, 'id = ?', array(1)); 3123 $this->assertEquals($clob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test CLOB set_field (full contents output disabled)'); 3124 $this->assertEquals($blob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test BLOB set_field (full contents output disabled)'); 3125 3126 // Empty data in binary columns works. 3127 $DB->set_field_select($tablename, 'onebinary', '', 'id = ?', array(1)); 3128 $this->assertEquals('', $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Blobs need to accept empty values.'); 3129 3130 // And "small" LOBs too, just in case. 3131 $newclob = substr($clob, 0, 500); 3132 $newblob = substr($blob, 0, 250); 3133 $DB->set_field_select($tablename, 'onetext', $newclob, 'id = ?', array(1)); 3134 $DB->set_field_select($tablename, 'onebinary', $newblob, 'id = ?', array(1)); 3135 $this->assertEquals($newclob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test "small" CLOB set_field (full contents output disabled)'); 3136 $this->assertEquals($newblob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test "small" BLOB set_field (full contents output disabled)'); 3137 3138 // This is the failure from MDL-24863. This was giving an error on MSSQL, 3139 // which converts the '1' to an integer, which cannot then be compared with 3140 // onetext cast to a varchar. This should be fixed and working now. 3141 $newchar = 'frog'; 3142 // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int). 3143 $params = array('onetext' => '1'); 3144 try { 3145 $DB->set_field_select($tablename, 'onechar', $newchar, $DB->sql_compare_text('onetext') . ' = ?', $params); 3146 $this->assertTrue(true, 'No exceptions thrown with numerical text param comparison for text field.'); 3147 } catch (dml_exception $e) { 3148 $this->assertFalse(true, 'We have an unexpected exception.'); 3149 throw $e; 3150 } 3151 } 3152 3153 public function test_count_records() { 3154 $DB = $this->tdb; 3155 3156 $dbman = $DB->get_manager(); 3157 3158 $table = $this->get_test_table(); 3159 $tablename = $table->getName(); 3160 3161 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3162 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3163 $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null); 3164 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3165 $dbman->create_table($table); 3166 3167 $this->assertSame(0, $DB->count_records($tablename)); 3168 3169 $DB->insert_record($tablename, array('course' => 3)); 3170 $DB->insert_record($tablename, array('course' => 4)); 3171 $DB->insert_record($tablename, array('course' => 5)); 3172 3173 $this->assertSame(3, $DB->count_records($tablename)); 3174 3175 // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int). 3176 $conditions = array('onetext' => '1'); 3177 try { 3178 $DB->count_records($tablename, $conditions); 3179 if (debugging()) { 3180 // Only in debug mode - hopefully all devs test code in debug mode... 3181 $this->fail('An Exception is missing, expected due to equating of text fields'); 3182 } 3183 } catch (moodle_exception $e) { 3184 $this->assertInstanceOf('dml_exception', $e); 3185 $this->assertSame('textconditionsnotallowed', $e->errorcode); 3186 } 3187 } 3188 3189 public function test_count_records_select() { 3190 $DB = $this->tdb; 3191 3192 $dbman = $DB->get_manager(); 3193 3194 $table = $this->get_test_table(); 3195 $tablename = $table->getName(); 3196 3197 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3198 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3199 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3200 $dbman->create_table($table); 3201 3202 $this->assertSame(0, $DB->count_records($tablename)); 3203 3204 $DB->insert_record($tablename, array('course' => 3)); 3205 $DB->insert_record($tablename, array('course' => 4)); 3206 $DB->insert_record($tablename, array('course' => 5)); 3207 3208 $this->assertSame(2, $DB->count_records_select($tablename, 'course > ?', array(3))); 3209 } 3210 3211 public function test_count_records_sql() { 3212 $DB = $this->tdb; 3213 $dbman = $DB->get_manager(); 3214 3215 $table = $this->get_test_table(); 3216 $tablename = $table->getName(); 3217 3218 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3219 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3220 $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null); 3221 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3222 $dbman->create_table($table); 3223 3224 $this->assertSame(0, $DB->count_records($tablename)); 3225 3226 $DB->insert_record($tablename, array('course' => 3, 'onechar' => 'a')); 3227 $DB->insert_record($tablename, array('course' => 4, 'onechar' => 'b')); 3228 $DB->insert_record($tablename, array('course' => 5, 'onechar' => 'c')); 3229 3230 $this->assertSame(2, $DB->count_records_sql("SELECT COUNT(*) FROM {{$tablename}} WHERE course > ?", array(3))); 3231 3232 // Test invalid use. 3233 try { 3234 $DB->count_records_sql("SELECT onechar FROM {{$tablename}} WHERE course = ?", array(3)); 3235 $this->fail('Exception expected when non-number field used in count_records_sql'); 3236 } catch (moodle_exception $e) { 3237 $this->assertInstanceOf('coding_exception', $e); 3238 } 3239 3240 try { 3241 $DB->count_records_sql("SELECT course FROM {{$tablename}} WHERE 1 = 2"); 3242 $this->fail('Exception expected when non-number field used in count_records_sql'); 3243 } catch (moodle_exception $e) { 3244 $this->assertInstanceOf('coding_exception', $e); 3245 } 3246 } 3247 3248 public function test_record_exists() { 3249 $DB = $this->tdb; 3250 $dbman = $DB->get_manager(); 3251 3252 $table = $this->get_test_table(); 3253 $tablename = $table->getName(); 3254 3255 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3256 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3257 $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null); 3258 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3259 $dbman->create_table($table); 3260 3261 $this->assertEquals(0, $DB->count_records($tablename)); 3262 3263 $this->assertFalse($DB->record_exists($tablename, array('course' => 3))); 3264 $DB->insert_record($tablename, array('course' => 3)); 3265 3266 $this->assertTrue($DB->record_exists($tablename, array('course' => 3))); 3267 3268 // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int). 3269 $conditions = array('onetext' => '1'); 3270 try { 3271 $DB->record_exists($tablename, $conditions); 3272 if (debugging()) { 3273 // Only in debug mode - hopefully all devs test code in debug mode... 3274 $this->fail('An Exception is missing, expected due to equating of text fields'); 3275 } 3276 } catch (moodle_exception $e) { 3277 $this->assertInstanceOf('dml_exception', $e); 3278 $this->assertSame('textconditionsnotallowed', $e->errorcode); 3279 } 3280 } 3281 3282 public function test_record_exists_select() { 3283 $DB = $this->tdb; 3284 $dbman = $DB->get_manager(); 3285 3286 $table = $this->get_test_table(); 3287 $tablename = $table->getName(); 3288 3289 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3290 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3291 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3292 $dbman->create_table($table); 3293 3294 $this->assertEquals(0, $DB->count_records($tablename)); 3295 3296 $this->assertFalse($DB->record_exists_select($tablename, "course = ?", array(3))); 3297 $DB->insert_record($tablename, array('course' => 3)); 3298 3299 $this->assertTrue($DB->record_exists_select($tablename, "course = ?", array(3))); 3300 } 3301 3302 public function test_record_exists_sql() { 3303 $DB = $this->tdb; 3304 $dbman = $DB->get_manager(); 3305 3306 $table = $this->get_test_table(); 3307 $tablename = $table->getName(); 3308 3309 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3310 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3311 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3312 $dbman->create_table($table); 3313 3314 $this->assertEquals(0, $DB->count_records($tablename)); 3315 3316 $this->assertFalse($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3))); 3317 $DB->insert_record($tablename, array('course' => 3)); 3318 3319 $this->assertTrue($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3))); 3320 } 3321 3322 public function test_recordset_locks_delete() { 3323 $DB = $this->tdb; 3324 $dbman = $DB->get_manager(); 3325 3326 // Setup. 3327 $table = $this->get_test_table(); 3328 $tablename = $table->getName(); 3329 3330 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3331 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3332 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3333 $dbman->create_table($table); 3334 3335 $DB->insert_record($tablename, array('course' => 1)); 3336 $DB->insert_record($tablename, array('course' => 2)); 3337 $DB->insert_record($tablename, array('course' => 3)); 3338 $DB->insert_record($tablename, array('course' => 4)); 3339 $DB->insert_record($tablename, array('course' => 5)); 3340 $DB->insert_record($tablename, array('course' => 6)); 3341 3342 // Test against db write locking while on an open recordset. 3343 $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // Get courses = {3,4}. 3344 foreach ($rs as $record) { 3345 $cid = $record->course; 3346 $DB->delete_records($tablename, array('course' => $cid)); 3347 $this->assertFalse($DB->record_exists($tablename, array('course' => $cid))); 3348 } 3349 $rs->close(); 3350 3351 $this->assertEquals(4, $DB->count_records($tablename, array())); 3352 } 3353 3354 public function test_recordset_locks_update() { 3355 $DB = $this->tdb; 3356 $dbman = $DB->get_manager(); 3357 3358 // Setup. 3359 $table = $this->get_test_table(); 3360 $tablename = $table->getName(); 3361 3362 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3363 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3364 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3365 $dbman->create_table($table); 3366 3367 $DB->insert_record($tablename, array('course' => 1)); 3368 $DB->insert_record($tablename, array('course' => 2)); 3369 $DB->insert_record($tablename, array('course' => 3)); 3370 $DB->insert_record($tablename, array('course' => 4)); 3371 $DB->insert_record($tablename, array('course' => 5)); 3372 $DB->insert_record($tablename, array('course' => 6)); 3373 3374 // Test against db write locking while on an open recordset. 3375 $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // Get courses = {3,4}. 3376 foreach ($rs as $record) { 3377 $cid = $record->course; 3378 $DB->set_field($tablename, 'course', 10, array('course' => $cid)); 3379 $this->assertFalse($DB->record_exists($tablename, array('course' => $cid))); 3380 } 3381 $rs->close(); 3382 3383 $this->assertEquals(2, $DB->count_records($tablename, array('course' => 10))); 3384 } 3385 3386 public function test_delete_records() { 3387 $DB = $this->tdb; 3388 $dbman = $DB->get_manager(); 3389 3390 $table = $this->get_test_table(); 3391 $tablename = $table->getName(); 3392 3393 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3394 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3395 $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null); 3396 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3397 $dbman->create_table($table); 3398 3399 $DB->insert_record($tablename, array('course' => 3)); 3400 $DB->insert_record($tablename, array('course' => 2)); 3401 $DB->insert_record($tablename, array('course' => 2)); 3402 3403 // Delete all records. 3404 $this->assertTrue($DB->delete_records($tablename)); 3405 $this->assertEquals(0, $DB->count_records($tablename)); 3406 3407 // Delete subset of records. 3408 $DB->insert_record($tablename, array('course' => 3)); 3409 $DB->insert_record($tablename, array('course' => 2)); 3410 $DB->insert_record($tablename, array('course' => 2)); 3411 3412 $this->assertTrue($DB->delete_records($tablename, array('course' => 2))); 3413 $this->assertEquals(1, $DB->count_records($tablename)); 3414 3415 // Delete all. 3416 $this->assertTrue($DB->delete_records($tablename, array())); 3417 $this->assertEquals(0, $DB->count_records($tablename)); 3418 3419 // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int). 3420 $conditions = array('onetext'=>'1'); 3421 try { 3422 $DB->delete_records($tablename, $conditions); 3423 if (debugging()) { 3424 // Only in debug mode - hopefully all devs test code in debug mode... 3425 $this->fail('An Exception is missing, expected due to equating of text fields'); 3426 } 3427 } catch (moodle_exception $e) { 3428 $this->assertInstanceOf('dml_exception', $e); 3429 $this->assertSame('textconditionsnotallowed', $e->errorcode); 3430 } 3431 3432 // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int). 3433 $conditions = array('onetext' => 1); 3434 try { 3435 $DB->delete_records($tablename, $conditions); 3436 if (debugging()) { 3437 // Only in debug mode - hopefully all devs test code in debug mode... 3438 $this->fail('An Exception is missing, expected due to equating of text fields'); 3439 } 3440 } catch (moodle_exception $e) { 3441 $this->assertInstanceOf('dml_exception', $e); 3442 $this->assertSame('textconditionsnotallowed', $e->errorcode); 3443 } 3444 } 3445 3446 public function test_delete_records_select() { 3447 $DB = $this->tdb; 3448 $dbman = $DB->get_manager(); 3449 3450 $table = $this->get_test_table(); 3451 $tablename = $table->getName(); 3452 3453 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3454 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3455 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3456 $dbman->create_table($table); 3457 3458 $DB->insert_record($tablename, array('course' => 3)); 3459 $DB->insert_record($tablename, array('course' => 2)); 3460 $DB->insert_record($tablename, array('course' => 2)); 3461 3462 $this->assertTrue($DB->delete_records_select($tablename, 'course = ?', array(2))); 3463 $this->assertEquals(1, $DB->count_records($tablename)); 3464 } 3465 3466 public function test_delete_records_subquery() { 3467 $DB = $this->tdb; 3468 $dbman = $DB->get_manager(); 3469 3470 $table = $this->get_test_table(); 3471 $tablename = $table->getName(); 3472 3473 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3474 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3475 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3476 $dbman->create_table($table); 3477 3478 $DB->insert_record($tablename, array('course' => 3)); 3479 $DB->insert_record($tablename, array('course' => 2)); 3480 $DB->insert_record($tablename, array('course' => 2)); 3481 3482 // This is not a useful scenario for using a subquery, but it will be sufficient for testing. 3483 // Use the 'frog' alias just to make it clearer when we are testing the alias parameter. 3484 $DB->delete_records_subquery($tablename, 'id', 'frog', 3485 'SELECT id AS frog FROM {' . $tablename . '} WHERE course = ?', [2]); 3486 $this->assertEquals(1, $DB->count_records($tablename)); 3487 } 3488 3489 public function test_delete_records_list() { 3490 $DB = $this->tdb; 3491 $dbman = $DB->get_manager(); 3492 3493 $table = $this->get_test_table(); 3494 $tablename = $table->getName(); 3495 3496 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3497 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3498 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3499 $dbman->create_table($table); 3500 3501 $DB->insert_record($tablename, array('course' => 1)); 3502 $DB->insert_record($tablename, array('course' => 2)); 3503 $DB->insert_record($tablename, array('course' => 3)); 3504 3505 $this->assertTrue($DB->delete_records_list($tablename, 'course', array(2, 3))); 3506 $this->assertEquals(1, $DB->count_records($tablename)); 3507 3508 $this->assertTrue($DB->delete_records_list($tablename, 'course', array())); // Must delete 0 rows without conditions. MDL-17645. 3509 $this->assertEquals(1, $DB->count_records($tablename)); 3510 } 3511 3512 public function test_object_params() { 3513 $DB = $this->tdb; 3514 $dbman = $DB->get_manager(); 3515 3516 $table = $this->get_test_table(); 3517 $tablename = $table->getName(); 3518 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3519 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3520 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3521 $dbman->create_table($table); 3522 3523 $o = new stdClass(); // Objects without __toString - never worked. 3524 try { 3525 $DB->fix_sql_params("SELECT {{$tablename}} WHERE course = ? ", array($o)); 3526 $this->fail('coding_exception expected'); 3527 } catch (moodle_exception $e) { 3528 $this->assertInstanceOf('coding_exception', $e); 3529 } 3530 3531 // Objects with __toString() forbidden everywhere since 2.3. 3532 $o = new dml_test_object_one(); 3533 try { 3534 $DB->fix_sql_params("SELECT {{$tablename}} WHERE course = ? ", array($o)); 3535 $this->fail('coding_exception expected'); 3536 } catch (moodle_exception $e) { 3537 $this->assertInstanceOf('coding_exception', $e); 3538 } 3539 3540 try { 3541 $DB->execute("SELECT {{$tablename}} WHERE course = ? ", array($o)); 3542 $this->fail('coding_exception expected'); 3543 } catch (moodle_exception $e) { 3544 $this->assertInstanceOf('coding_exception', $e); 3545 } 3546 3547 try { 3548 $DB->get_recordset_sql("SELECT {{$tablename}} WHERE course = ? ", array($o)); 3549 $this->fail('coding_exception expected'); 3550 } catch (moodle_exception $e) { 3551 $this->assertInstanceOf('coding_exception', $e); 3552 } 3553 3554 try { 3555 $DB->get_records_sql("SELECT {{$tablename}} WHERE course = ? ", array($o)); 3556 $this->fail('coding_exception expected'); 3557 } catch (moodle_exception $e) { 3558 $this->assertInstanceOf('coding_exception', $e); 3559 } 3560 3561 try { 3562 $record = new stdClass(); 3563 $record->course = $o; 3564 $DB->insert_record_raw($tablename, $record); 3565 $this->fail('coding_exception expected'); 3566 } catch (moodle_exception $e) { 3567 $this->assertInstanceOf('coding_exception', $e); 3568 } 3569 3570 try { 3571 $record = new stdClass(); 3572 $record->course = $o; 3573 $DB->insert_record($tablename, $record); 3574 $this->fail('coding_exception expected'); 3575 } catch (moodle_exception $e) { 3576 $this->assertInstanceOf('coding_exception', $e); 3577 } 3578 3579 try { 3580 $record = new stdClass(); 3581 $record->course = $o; 3582 $DB->import_record($tablename, $record); 3583 $this->fail('coding_exception expected'); 3584 } catch (moodle_exception $e) { 3585 $this->assertInstanceOf('coding_exception', $e); 3586 } 3587 3588 try { 3589 $record = new stdClass(); 3590 $record->id = 1; 3591 $record->course = $o; 3592 $DB->update_record_raw($tablename, $record); 3593 $this->fail('coding_exception expected'); 3594 } catch (moodle_exception $e) { 3595 $this->assertInstanceOf('coding_exception', $e); 3596 } 3597 3598 try { 3599 $record = new stdClass(); 3600 $record->id = 1; 3601 $record->course = $o; 3602 $DB->update_record($tablename, $record); 3603 $this->fail('coding_exception expected'); 3604 } catch (moodle_exception $e) { 3605 $this->assertInstanceOf('coding_exception', $e); 3606 } 3607 3608 try { 3609 $DB->set_field_select($tablename, 'course', 1, "course = ? ", array($o)); 3610 $this->fail('coding_exception expected'); 3611 } catch (moodle_exception $e) { 3612 $this->assertInstanceOf('coding_exception', $e); 3613 } 3614 3615 try { 3616 $DB->delete_records_select($tablename, "course = ? ", array($o)); 3617 $this->fail('coding_exception expected'); 3618 } catch (moodle_exception $e) { 3619 $this->assertInstanceOf('coding_exception', $e); 3620 } 3621 } 3622 3623 public function test_sql_null_from_clause() { 3624 $DB = $this->tdb; 3625 $sql = "SELECT 1 AS id ".$DB->sql_null_from_clause(); 3626 $this->assertEquals(1, $DB->get_field_sql($sql)); 3627 } 3628 3629 public function test_sql_bitand() { 3630 $DB = $this->tdb; 3631 $dbman = $DB->get_manager(); 3632 3633 $table = $this->get_test_table(); 3634 $tablename = $table->getName(); 3635 3636 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3637 $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3638 $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3639 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3640 $dbman->create_table($table); 3641 3642 $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10)); 3643 3644 $sql = "SELECT ".$DB->sql_bitand(10, 3)." AS res ".$DB->sql_null_from_clause(); 3645 $this->assertEquals(2, $DB->get_field_sql($sql)); 3646 3647 $sql = "SELECT id, ".$DB->sql_bitand('col1', 'col2')." AS res FROM {{$tablename}}"; 3648 $result = $DB->get_records_sql($sql); 3649 $this->assertCount(1, $result); 3650 $this->assertEquals(2, reset($result)->res); 3651 3652 $sql = "SELECT id, ".$DB->sql_bitand('col1', '?')." AS res FROM {{$tablename}}"; 3653 $result = $DB->get_records_sql($sql, array(10)); 3654 $this->assertCount(1, $result); 3655 $this->assertEquals(2, reset($result)->res); 3656 } 3657 3658 public function test_sql_bitnot() { 3659 $DB = $this->tdb; 3660 3661 $not = $DB->sql_bitnot(2); 3662 $notlimited = $DB->sql_bitand($not, 7); // Might be positive or negative number which can not fit into PHP INT! 3663 3664 $sql = "SELECT $notlimited AS res ".$DB->sql_null_from_clause(); 3665 $this->assertEquals(5, $DB->get_field_sql($sql)); 3666 } 3667 3668 public function test_sql_bitor() { 3669 $DB = $this->tdb; 3670 $dbman = $DB->get_manager(); 3671 3672 $table = $this->get_test_table(); 3673 $tablename = $table->getName(); 3674 3675 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3676 $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3677 $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3678 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3679 $dbman->create_table($table); 3680 3681 $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10)); 3682 3683 $sql = "SELECT ".$DB->sql_bitor(10, 3)." AS res ".$DB->sql_null_from_clause(); 3684 $this->assertEquals(11, $DB->get_field_sql($sql)); 3685 3686 $sql = "SELECT id, ".$DB->sql_bitor('col1', 'col2')." AS res FROM {{$tablename}}"; 3687 $result = $DB->get_records_sql($sql); 3688 $this->assertCount(1, $result); 3689 $this->assertEquals(11, reset($result)->res); 3690 3691 $sql = "SELECT id, ".$DB->sql_bitor('col1', '?')." AS res FROM {{$tablename}}"; 3692 $result = $DB->get_records_sql($sql, array(10)); 3693 $this->assertCount(1, $result); 3694 $this->assertEquals(11, reset($result)->res); 3695 } 3696 3697 public function test_sql_bitxor() { 3698 $DB = $this->tdb; 3699 $dbman = $DB->get_manager(); 3700 3701 $table = $this->get_test_table(); 3702 $tablename = $table->getName(); 3703 3704 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3705 $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3706 $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3707 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3708 $dbman->create_table($table); 3709 3710 $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10)); 3711 3712 $sql = "SELECT ".$DB->sql_bitxor(10, 3)." AS res ".$DB->sql_null_from_clause(); 3713 $this->assertEquals(9, $DB->get_field_sql($sql)); 3714 3715 $sql = "SELECT id, ".$DB->sql_bitxor('col1', 'col2')." AS res FROM {{$tablename}}"; 3716 $result = $DB->get_records_sql($sql); 3717 $this->assertCount(1, $result); 3718 $this->assertEquals(9, reset($result)->res); 3719 3720 $sql = "SELECT id, ".$DB->sql_bitxor('col1', '?')." AS res FROM {{$tablename}}"; 3721 $result = $DB->get_records_sql($sql, array(10)); 3722 $this->assertCount(1, $result); 3723 $this->assertEquals(9, reset($result)->res); 3724 } 3725 3726 public function test_sql_modulo() { 3727 $DB = $this->tdb; 3728 $sql = "SELECT ".$DB->sql_modulo(10, 7)." AS res ".$DB->sql_null_from_clause(); 3729 $this->assertEquals(3, $DB->get_field_sql($sql)); 3730 } 3731 3732 public function test_sql_ceil() { 3733 $DB = $this->tdb; 3734 $sql = "SELECT ".$DB->sql_ceil(665.666)." AS res ".$DB->sql_null_from_clause(); 3735 $this->assertEquals(666, $DB->get_field_sql($sql)); 3736 } 3737 3738 public function test_cast_char2int() { 3739 $DB = $this->tdb; 3740 $dbman = $DB->get_manager(); 3741 3742 $table1 = $this->get_test_table("1"); 3743 $tablename1 = $table1->getName(); 3744 3745 $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3746 $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 3747 $table1->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null); 3748 $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3749 $dbman->create_table($table1); 3750 3751 $DB->insert_record($tablename1, array('name'=>'0100', 'nametext'=>'0200')); 3752 $DB->insert_record($tablename1, array('name'=>'10', 'nametext'=>'20')); 3753 3754 $table2 = $this->get_test_table("2"); 3755 $tablename2 = $table2->getName(); 3756 $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3757 $table2->add_field('res', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3758 $table2->add_field('restext', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 3759 $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3760 $dbman->create_table($table2); 3761 3762 $DB->insert_record($tablename2, array('res'=>100, 'restext'=>200)); 3763 3764 // Casting varchar field. 3765 $sql = "SELECT * 3766 FROM {".$tablename1."} t1 3767 JOIN {".$tablename2."} t2 ON ".$DB->sql_cast_char2int("t1.name")." = t2.res "; 3768 $records = $DB->get_records_sql($sql); 3769 $this->assertCount(1, $records); 3770 // Also test them in order clauses. 3771 $sql = "SELECT * FROM {{$tablename1}} ORDER BY ".$DB->sql_cast_char2int('name'); 3772 $records = $DB->get_records_sql($sql); 3773 $this->assertCount(2, $records); 3774 $this->assertSame('10', reset($records)->name); 3775 $this->assertSame('0100', next($records)->name); 3776 3777 // Casting text field. 3778 $sql = "SELECT * 3779 FROM {".$tablename1."} t1 3780 JOIN {".$tablename2."} t2 ON ".$DB->sql_cast_char2int("t1.nametext", true)." = t2.restext "; 3781 $records = $DB->get_records_sql($sql); 3782 $this->assertCount(1, $records); 3783 // Also test them in order clauses. 3784 $sql = "SELECT * FROM {{$tablename1}} ORDER BY ".$DB->sql_cast_char2int('nametext', true); 3785 $records = $DB->get_records_sql($sql); 3786 $this->assertCount(2, $records); 3787 $this->assertSame('20', reset($records)->nametext); 3788 $this->assertSame('0200', next($records)->nametext); 3789 } 3790 3791 public function test_cast_char2real() { 3792 $DB = $this->tdb; 3793 $dbman = $DB->get_manager(); 3794 3795 $table = $this->get_test_table(); 3796 $tablename = $table->getName(); 3797 3798 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3799 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 3800 $table->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null); 3801 $table->add_field('res', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null); 3802 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3803 $dbman->create_table($table); 3804 3805 $DB->insert_record($tablename, array('name'=>'10.10', 'nametext'=>'10.10', 'res'=>5.1)); 3806 $DB->insert_record($tablename, array('name'=>'91.10', 'nametext'=>'91.10', 'res'=>666)); 3807 $DB->insert_record($tablename, array('name'=>'011.13333333', 'nametext'=>'011.13333333', 'res'=>10.1)); 3808 3809 // Casting varchar field. 3810 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_cast_char2real('name')." > res"; 3811 $records = $DB->get_records_sql($sql); 3812 $this->assertCount(2, $records); 3813 // Also test them in order clauses. 3814 $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_cast_char2real('name'); 3815 $records = $DB->get_records_sql($sql); 3816 $this->assertCount(3, $records); 3817 $this->assertSame('10.10', reset($records)->name); 3818 $this->assertSame('011.13333333', next($records)->name); 3819 $this->assertSame('91.10', next($records)->name); 3820 // And verify we can operate with them without too much problem with at least 6 decimals scale accuracy. 3821 $sql = "SELECT AVG(" . $DB->sql_cast_char2real('name') . ") FROM {{$tablename}}"; 3822 $this->assertEquals(37.44444443333333, (float)$DB->get_field_sql($sql), '', 1.0E-6); 3823 3824 // Casting text field. 3825 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_cast_char2real('nametext', true)." > res"; 3826 $records = $DB->get_records_sql($sql); 3827 $this->assertCount(2, $records); 3828 // Also test them in order clauses. 3829 $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_cast_char2real('nametext', true); 3830 $records = $DB->get_records_sql($sql); 3831 $this->assertCount(3, $records); 3832 $this->assertSame('10.10', reset($records)->nametext); 3833 $this->assertSame('011.13333333', next($records)->nametext); 3834 $this->assertSame('91.10', next($records)->nametext); 3835 // And verify we can operate with them without too much problem with at least 6 decimals scale accuracy. 3836 $sql = "SELECT AVG(" . $DB->sql_cast_char2real('nametext', true) . ") FROM {{$tablename}}"; 3837 $this->assertEquals(37.44444443333333, (float)$DB->get_field_sql($sql), '', 1.0E-6); 3838 3839 // Check it works with values passed as param. 3840 $sql = "SELECT name FROM {{$tablename}} WHERE FLOOR(res - " . $DB->sql_cast_char2real(':param') . ") = 0"; 3841 $this->assertEquals('011.13333333', $DB->get_field_sql($sql, array('param' => '10.09999'))); 3842 3843 // And also, although not recommended, with directly passed values. 3844 $sql = "SELECT name FROM {{$tablename}} WHERE FLOOR(res - " . $DB->sql_cast_char2real('10.09999') . ") = 0"; 3845 $this->assertEquals('011.13333333', $DB->get_field_sql($sql)); 3846 } 3847 3848 public function test_sql_compare_text() { 3849 $DB = $this->tdb; 3850 $dbman = $DB->get_manager(); 3851 3852 $table = $this->get_test_table(); 3853 $tablename = $table->getName(); 3854 3855 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3856 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 3857 $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null); 3858 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3859 $dbman->create_table($table); 3860 3861 $DB->insert_record($tablename, array('name'=>'abcd', 'description'=>'abcd')); 3862 $DB->insert_record($tablename, array('name'=>'abcdef', 'description'=>'bbcdef')); 3863 $DB->insert_record($tablename, array('name'=>'aaaa', 'description'=>'aaaacccccccccccccccccc')); 3864 $DB->insert_record($tablename, array('name'=>'xxxx', 'description'=>'123456789a123456789b123456789c123456789d')); 3865 3866 // Only some supported databases truncate TEXT fields for comparisons, currently MSSQL and Oracle. 3867 $dbtruncatestextfields = ($DB->get_dbfamily() == 'mssql' || $DB->get_dbfamily() == 'oracle'); 3868 3869 if ($dbtruncatestextfields) { 3870 // Ensure truncation behaves as expected. 3871 3872 $sql = "SELECT " . $DB->sql_compare_text('description') . " AS field FROM {{$tablename}} WHERE name = ?"; 3873 $description = $DB->get_field_sql($sql, array('xxxx')); 3874 3875 // Should truncate to 32 chars (the default). 3876 $this->assertEquals('123456789a123456789b123456789c12', $description); 3877 3878 $sql = "SELECT " . $DB->sql_compare_text('description', 35) . " AS field FROM {{$tablename}} WHERE name = ?"; 3879 $description = $DB->get_field_sql($sql, array('xxxx')); 3880 3881 // Should truncate to the specified number of chars. 3882 $this->assertEquals('123456789a123456789b123456789c12345', $description); 3883 } 3884 3885 // Ensure text field comparison is successful. 3886 $sql = "SELECT * FROM {{$tablename}} WHERE name = ".$DB->sql_compare_text('description'); 3887 $records = $DB->get_records_sql($sql); 3888 $this->assertCount(1, $records); 3889 3890 $sql = "SELECT * FROM {{$tablename}} WHERE name = ".$DB->sql_compare_text('description', 4); 3891 $records = $DB->get_records_sql($sql); 3892 if ($dbtruncatestextfields) { 3893 // Should truncate description to 4 characters before comparing. 3894 $this->assertCount(2, $records); 3895 } else { 3896 // Should leave untruncated, so one less match. 3897 $this->assertCount(1, $records); 3898 } 3899 3900 // Now test the function with really big content and params. 3901 $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt'); 3902 $DB->insert_record($tablename, array('name' => 'zzzz', 'description' => $clob)); 3903 $sql = "SELECT * FROM {{$tablename}} 3904 WHERE " . $DB->sql_compare_text('description') . " = " . $DB->sql_compare_text(':clob'); 3905 $records = $DB->get_records_sql($sql, array('clob' => $clob)); 3906 $this->assertCount(1, $records); 3907 $record = reset($records); 3908 $this->assertSame($clob, $record->description); 3909 } 3910 3911 public function test_unique_index_collation_trouble() { 3912 // Note: this is a work in progress, we should probably move this to ddl test. 3913 3914 $DB = $this->tdb; 3915 $dbman = $DB->get_manager(); 3916 3917 $table = $this->get_test_table(); 3918 $tablename = $table->getName(); 3919 3920 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3921 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 3922 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3923 $table->add_index('name', XMLDB_INDEX_UNIQUE, array('name')); 3924 $dbman->create_table($table); 3925 3926 $DB->insert_record($tablename, array('name'=>'aaa')); 3927 3928 try { 3929 $DB->insert_record($tablename, array('name'=>'AAA')); 3930 } catch (moodle_exception $e) { 3931 // TODO: ignore case insensitive uniqueness problems for now. 3932 // $this->fail("Unique index is case sensitive - this may cause problems in some tables"); 3933 } 3934 3935 try { 3936 $DB->insert_record($tablename, array('name'=>'aäa')); 3937 $DB->insert_record($tablename, array('name'=>'aáa')); 3938 $this->assertTrue(true); 3939 } catch (moodle_exception $e) { 3940 $family = $DB->get_dbfamily(); 3941 if ($family === 'mysql' or $family === 'mssql') { 3942 $this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages. This is usually caused by accent insensitive default collation."); 3943 } else { 3944 // This should not happen, PostgreSQL and Oracle do not support accent insensitive uniqueness. 3945 $this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages."); 3946 } 3947 throw($e); 3948 } 3949 } 3950 3951 public function test_sql_equal() { 3952 $DB = $this->tdb; 3953 $dbman = $DB->get_manager(); 3954 3955 $table = $this->get_test_table(); 3956 $tablename = $table->getName(); 3957 3958 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 3959 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 3960 $table->add_field('name2', XMLDB_TYPE_CHAR, '255', null, null, null, null); 3961 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 3962 $dbman->create_table($table); 3963 3964 $DB->insert_record($tablename, array('name' => 'one', 'name2' => 'one')); 3965 $DB->insert_record($tablename, array('name' => 'ONE', 'name2' => 'ONE')); 3966 $DB->insert_record($tablename, array('name' => 'two', 'name2' => 'TWO')); 3967 $DB->insert_record($tablename, array('name' => 'öne', 'name2' => 'one')); 3968 $DB->insert_record($tablename, array('name' => 'öne', 'name2' => 'ÖNE')); 3969 3970 // Case sensitive and accent sensitive (equal and not equal). 3971 $sql = "SELECT * FROM {{$tablename}} WHERE " . $DB->sql_equal('name', '?', true, true, false); 3972 $records = $DB->get_records_sql($sql, array('one')); 3973 $this->assertCount(1, $records); 3974 $sql = "SELECT * FROM {{$tablename}} WHERE " . $DB->sql_equal('name', ':name', true, true, true); 3975 $records = $DB->get_records_sql($sql, array('name' => 'one')); 3976 $this->assertCount(4, $records); 3977 // And with column comparison instead of params. 3978 $sql = "SELECT * FROM {{$tablename}} WHERE " . $DB->sql_equal('name', 'name2', true, true, false); 3979 $records = $DB->get_records_sql($sql); 3980 $this->assertCount(2, $records); 3981 3982 // Case insensitive and accent sensitive (equal and not equal). 3983 $sql = "SELECT * FROM {{$tablename}} WHERE " . $DB->sql_equal('name', '?', false, true, false); 3984 $records = $DB->get_records_sql($sql, array('one')); 3985 $this->assertCount(2, $records); 3986 $sql = "SELECT * FROM {{$tablename}} WHERE " . $DB->sql_equal('name', ':name', false, true, true); 3987 $records = $DB->get_records_sql($sql, array('name' => 'one')); 3988 $this->assertCount(3, $records); 3989 // And with column comparison instead of params. 3990 $sql = "SELECT * FROM {{$tablename}} WHERE " . $DB->sql_equal('name', 'name2', false, true, false); 3991 $records = $DB->get_records_sql($sql); 3992 $this->assertCount(4, $records); 3993 3994 // TODO: Accent insensitive is not cross-db, only some drivers support it, so just verify the queries work. 3995 $sql = "SELECT * FROM {{$tablename}} WHERE " . $DB->sql_equal('name', '?', true, false); 3996 $records = $DB->get_records_sql($sql, array('one')); 3997 $this->assertGreaterThanOrEqual(1, count($records)); // At very least, there is 1 record with CS/AI "one". 3998 $sql = "SELECT * FROM {{$tablename}} WHERE " . $DB->sql_equal('name', '?', false, false); 3999 $records = $DB->get_records_sql($sql, array('one')); 4000 $this->assertGreaterThanOrEqual(2, count($records)); // At very least, there are 2 records with CI/AI "one". 4001 // And with column comparison instead of params. 4002 $sql = "SELECT * FROM {{$tablename}} WHERE " . $DB->sql_equal('name', 'name2', false, false); 4003 $records = $DB->get_records_sql($sql); 4004 $this->assertGreaterThanOrEqual(4, count($records)); // At very least, there are 4 records with CI/AI names matching. 4005 } 4006 4007 public function test_sql_like() { 4008 $DB = $this->tdb; 4009 $dbman = $DB->get_manager(); 4010 4011 $table = $this->get_test_table(); 4012 $tablename = $table->getName(); 4013 4014 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4015 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 4016 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4017 $dbman->create_table($table); 4018 4019 $DB->insert_record($tablename, array('name'=>'SuperDuperRecord')); 4020 $DB->insert_record($tablename, array('name'=>'Nodupor')); 4021 $DB->insert_record($tablename, array('name'=>'ouch')); 4022 $DB->insert_record($tablename, array('name'=>'ouc_')); 4023 $DB->insert_record($tablename, array('name'=>'ouc%')); 4024 $DB->insert_record($tablename, array('name'=>'aui')); 4025 $DB->insert_record($tablename, array('name'=>'aüi')); 4026 $DB->insert_record($tablename, array('name'=>'aÜi')); 4027 4028 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false); 4029 $records = $DB->get_records_sql($sql, array("%dup_r%")); 4030 $this->assertCount(2, $records); 4031 4032 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true); 4033 $records = $DB->get_records_sql($sql, array("%dup%")); 4034 $this->assertCount(1, $records); 4035 4036 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?'); // Defaults. 4037 $records = $DB->get_records_sql($sql, array("%dup%")); 4038 $this->assertCount(1, $records); 4039 4040 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true); 4041 $records = $DB->get_records_sql($sql, array("ouc\\_")); 4042 $this->assertCount(1, $records); 4043 4044 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, false, '|'); 4045 $records = $DB->get_records_sql($sql, array($DB->sql_like_escape("ouc%", '|'))); 4046 $this->assertCount(1, $records); 4047 4048 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true); 4049 $records = $DB->get_records_sql($sql, array('aui')); 4050 $this->assertCount(1, $records); 4051 4052 // Test LIKE under unusual collations. 4053 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false, false); 4054 $records = $DB->get_records_sql($sql, array("%dup_r%")); 4055 $this->assertCount(2, $records); 4056 4057 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, true); // NOT LIKE. 4058 $records = $DB->get_records_sql($sql, array("%o%")); 4059 $this->assertCount(3, $records); 4060 4061 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false, true, true); // NOT ILIKE. 4062 $records = $DB->get_records_sql($sql, array("%D%")); 4063 $this->assertCount(6, $records); 4064 4065 // Verify usual escaping characters work fine. 4066 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, false, '\\'); 4067 $records = $DB->get_records_sql($sql, array("ouc\\_")); 4068 $this->assertCount(1, $records); 4069 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, false, '|'); 4070 $records = $DB->get_records_sql($sql, array("ouc|%")); 4071 $this->assertCount(1, $records); 4072 4073 // TODO: we do not require accent insensitivness yet, just make sure it does not throw errors. 4074 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, false); 4075 $records = $DB->get_records_sql($sql, array('aui')); 4076 // $this->assertEquals(2, count($records), 'Accent insensitive LIKE searches may not be supported in all databases, this is not a problem.'); 4077 $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false, false); 4078 $records = $DB->get_records_sql($sql, array('aui')); 4079 // $this->assertEquals(3, count($records), 'Accent insensitive LIKE searches may not be supported in all databases, this is not a problem.'); 4080 } 4081 4082 /** 4083 * Test DML libraries sql_like_escape method 4084 */ 4085 public function test_sql_like_escape(): void { 4086 $DB = $this->tdb; 4087 $dbman = $DB->get_manager(); 4088 4089 $table = $this->get_test_table(); 4090 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4091 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 4092 $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); 4093 $dbman->create_table($table); 4094 4095 $tablename = $table->getName(); 4096 4097 // Two of the records contain LIKE characters (%_), plus square brackets supported only by SQL Server (and '^-' which 4098 // should be ignored by SQL Server given they only have meaning inside square brackets). 4099 $DB->insert_record($tablename, (object) ['name' => 'lionel']); 4100 $DB->insert_record($tablename, (object) ['name' => 'lionel%_^-[0]']); 4101 $DB->insert_record($tablename, (object) ['name' => 'rick']); 4102 $DB->insert_record($tablename, (object) ['name' => 'rick%_^-[0]']); 4103 4104 $select = $DB->sql_like('name', ':namelike'); 4105 $params = ['namelike' => '%' . $DB->sql_like_escape('%_^-[0]')]; 4106 4107 // All drivers should return our two records containing wildcard characters. 4108 $this->assertEqualsCanonicalizing([ 4109 'lionel%_^-[0]', 4110 'rick%_^-[0]', 4111 ], $DB->get_fieldset_select($tablename, 'name', $select, $params)); 4112 4113 // Test for unbalanced brackets. 4114 $select = $DB->sql_like('name', ':namelike'); 4115 $params = ['namelike' => '%' . $DB->sql_like_escape('[') . '%']; 4116 4117 $this->assertEqualsCanonicalizing([ 4118 'lionel%_^-[0]', 4119 'rick%_^-[0]', 4120 ], $DB->get_fieldset_select($tablename, 'name', $select, $params)); 4121 } 4122 4123 public function test_coalesce() { 4124 $DB = $this->tdb; 4125 4126 // Testing not-null occurrences, return 1st. 4127 $sql = "SELECT COALESCE('returnthis', 'orthis', 'orwhynotthis') AS test" . $DB->sql_null_from_clause(); 4128 $this->assertSame('returnthis', $DB->get_field_sql($sql, array())); 4129 $sql = "SELECT COALESCE(:paramvalue, 'orthis', 'orwhynotthis') AS test" . $DB->sql_null_from_clause(); 4130 $this->assertSame('returnthis', $DB->get_field_sql($sql, array('paramvalue' => 'returnthis'))); 4131 4132 // Testing null occurrences, return 2nd. 4133 $sql = "SELECT COALESCE(null, 'returnthis', 'orthis') AS test" . $DB->sql_null_from_clause(); 4134 $this->assertSame('returnthis', $DB->get_field_sql($sql, array())); 4135 $sql = "SELECT COALESCE(:paramvalue, 'returnthis', 'orthis') AS test" . $DB->sql_null_from_clause(); 4136 $this->assertSame('returnthis', $DB->get_field_sql($sql, array('paramvalue' => null))); 4137 $sql = "SELECT COALESCE(null, :paramvalue, 'orthis') AS test" . $DB->sql_null_from_clause(); 4138 $this->assertSame('returnthis', $DB->get_field_sql($sql, array('paramvalue' => 'returnthis'))); 4139 4140 // Testing null occurrences, return 3rd. 4141 $sql = "SELECT COALESCE(null, null, 'returnthis') AS test" . $DB->sql_null_from_clause(); 4142 $this->assertSame('returnthis', $DB->get_field_sql($sql, array())); 4143 $sql = "SELECT COALESCE(null, :paramvalue, 'returnthis') AS test" . $DB->sql_null_from_clause(); 4144 $this->assertSame('returnthis', $DB->get_field_sql($sql, array('paramvalue' => null))); 4145 $sql = "SELECT COALESCE(null, null, :paramvalue) AS test" . $DB->sql_null_from_clause(); 4146 $this->assertSame('returnthis', $DB->get_field_sql($sql, array('paramvalue' => 'returnthis'))); 4147 4148 // Testing all null occurrences, return null. 4149 // Note: under mssql, if all elements are nulls, at least one must be a "typed" null, hence 4150 // we cannot test this in a cross-db way easily, so next 2 tests are using 4151 // different queries depending of the DB family. 4152 $customnull = $DB->get_dbfamily() == 'mssql' ? 'CAST(null AS varchar)' : 'null'; 4153 $sql = "SELECT COALESCE(null, null, " . $customnull . ") AS test" . $DB->sql_null_from_clause(); 4154 $this->assertNull($DB->get_field_sql($sql, array())); 4155 $sql = "SELECT COALESCE(null, :paramvalue, " . $customnull . ") AS test" . $DB->sql_null_from_clause(); 4156 $this->assertNull($DB->get_field_sql($sql, array('paramvalue' => null))); 4157 4158 // Check there are not problems with whitespace strings. 4159 $sql = "SELECT COALESCE(null, :paramvalue, null) AS test" . $DB->sql_null_from_clause(); 4160 $this->assertSame('', $DB->get_field_sql($sql, array('paramvalue' => ''))); 4161 } 4162 4163 public function test_sql_concat() { 4164 $DB = $this->tdb; 4165 $dbman = $DB->get_manager(); 4166 4167 // Testing all sort of values. 4168 $sql = "SELECT ".$DB->sql_concat("?", "?", "?")." AS fullname ". $DB->sql_null_from_clause(); 4169 // String, some unicode chars. 4170 $params = array('name', 'áéíóú', 'name3'); 4171 $this->assertSame('nameáéíóúname3', $DB->get_field_sql($sql, $params)); 4172 // String, spaces and numbers. 4173 $params = array('name', ' ', 12345); 4174 $this->assertSame('name 12345', $DB->get_field_sql($sql, $params)); 4175 // Float, empty and strings. 4176 $params = array(123.45, '', 'test'); 4177 $this->assertSame('123.45test', $DB->get_field_sql($sql, $params)); 4178 // Only integers. 4179 $params = array(12, 34, 56); 4180 $this->assertSame('123456', $DB->get_field_sql($sql, $params)); 4181 // Float, null and strings. 4182 $params = array(123.45, null, 'test'); 4183 $this->assertNull($DB->get_field_sql($sql, $params)); // Concatenate null with anything result = null. 4184 4185 // Testing fieldnames + values and also integer fieldnames. 4186 $table = $this->get_test_table(); 4187 $tablename = $table->getName(); 4188 4189 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4190 $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null); 4191 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4192 $dbman->create_table($table); 4193 4194 $DB->insert_record($tablename, array('description'=>'áéíóú')); 4195 $DB->insert_record($tablename, array('description'=>'dxxx')); 4196 $DB->insert_record($tablename, array('description'=>'bcde')); 4197 4198 // Fieldnames and values mixed. 4199 $sql = 'SELECT id, ' . $DB->sql_concat('description', "'harcoded'", '?', '?') . ' AS result FROM {' . $tablename . '}'; 4200 $records = $DB->get_records_sql($sql, array(123.45, 'test')); 4201 $this->assertCount(3, $records); 4202 $this->assertSame('áéíóúharcoded123.45test', $records[1]->result); 4203 // Integer fieldnames and values. 4204 $sql = 'SELECT id, ' . $DB->sql_concat('id', "'harcoded'", '?', '?') . ' AS result FROM {' . $tablename . '}'; 4205 $records = $DB->get_records_sql($sql, array(123.45, 'test')); 4206 $this->assertCount(3, $records); 4207 $this->assertSame('1harcoded123.45test', $records[1]->result); 4208 // All integer fieldnames. 4209 $sql = 'SELECT id, ' . $DB->sql_concat('id', 'id', 'id') . ' AS result FROM {' . $tablename . '}'; 4210 $records = $DB->get_records_sql($sql, array()); 4211 $this->assertCount(3, $records); 4212 $this->assertSame('111', $records[1]->result); 4213 4214 } 4215 4216 public function sql_concat_join_provider() { 4217 return array( 4218 // All strings. 4219 array( 4220 "' '", 4221 array("'name'", "'name2'", "'name3'"), 4222 array(), 4223 'name name2 name3', 4224 ), 4225 // All strings using placeholders 4226 array( 4227 "' '", 4228 array("?", "?", "?"), 4229 array('name', 'name2', 'name3'), 4230 'name name2 name3', 4231 ), 4232 // All integers. 4233 array( 4234 "' '", 4235 array(1, 2, 3), 4236 array(), 4237 '1 2 3', 4238 ), 4239 // All integers using placeholders 4240 array( 4241 "' '", 4242 array("?", "?", "?"), 4243 array(1, 2, 3), 4244 '1 2 3', 4245 ), 4246 // Mix of strings and integers. 4247 array( 4248 "' '", 4249 array(1, "'2'", 3), 4250 array(), 4251 '1 2 3', 4252 ), 4253 // Mix of strings and integers using placeholders. 4254 array( 4255 "' '", 4256 array(1, '2', 3), 4257 array(), 4258 '1 2 3', 4259 ), 4260 ); 4261 } 4262 4263 /** 4264 * @dataProvider sql_concat_join_provider 4265 * @param string $concat The string to use when concatanating. 4266 * @param array $fields The fields to concatanate 4267 * @param array $params Any parameters to provide to the query 4268 * @param @string $expected The expected result 4269 */ 4270 public function test_concat_join($concat, $fields, $params, $expected) { 4271 $DB = $this->tdb; 4272 $sql = "SELECT " . $DB->sql_concat_join($concat, $fields) . " AS result" . $DB->sql_null_from_clause(); 4273 $result = $DB->get_field_sql($sql, $params); 4274 $this->assertEquals($expected, $result); 4275 } 4276 4277 public function test_sql_fullname() { 4278 $DB = $this->tdb; 4279 $sql = "SELECT ".$DB->sql_fullname(':first', ':last')." AS fullname ".$DB->sql_null_from_clause(); 4280 $params = array('first'=>'Firstname', 'last'=>'Surname'); 4281 $this->assertEquals("Firstname Surname", $DB->get_field_sql($sql, $params)); 4282 } 4283 4284 public function test_sql_order_by_text() { 4285 $DB = $this->tdb; 4286 $dbman = $DB->get_manager(); 4287 4288 $table = $this->get_test_table(); 4289 $tablename = $table->getName(); 4290 4291 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4292 $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null); 4293 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4294 $dbman->create_table($table); 4295 4296 $DB->insert_record($tablename, array('description'=>'abcd')); 4297 $DB->insert_record($tablename, array('description'=>'dxxx')); 4298 $DB->insert_record($tablename, array('description'=>'bcde')); 4299 4300 $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_order_by_text('description'); 4301 $records = $DB->get_records_sql($sql); 4302 $first = array_shift($records); 4303 $this->assertEquals(1, $first->id); 4304 $second = array_shift($records); 4305 $this->assertEquals(3, $second->id); 4306 $last = array_shift($records); 4307 $this->assertEquals(2, $last->id); 4308 } 4309 4310 public function test_sql_substring() { 4311 $DB = $this->tdb; 4312 $dbman = $DB->get_manager(); 4313 4314 $table = $this->get_test_table(); 4315 $tablename = $table->getName(); 4316 4317 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4318 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 4319 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4320 $dbman->create_table($table); 4321 4322 $string = 'abcdefghij'; 4323 4324 $DB->insert_record($tablename, array('name'=>$string)); 4325 4326 $sql = "SELECT id, ".$DB->sql_substr("name", 5)." AS name FROM {{$tablename}}"; 4327 $record = $DB->get_record_sql($sql); 4328 $this->assertEquals(substr($string, 5-1), $record->name); 4329 4330 $sql = "SELECT id, ".$DB->sql_substr("name", 5, 2)." AS name FROM {{$tablename}}"; 4331 $record = $DB->get_record_sql($sql); 4332 $this->assertEquals(substr($string, 5-1, 2), $record->name); 4333 4334 try { 4335 // Silence php warning. 4336 @$DB->sql_substr("name"); 4337 $this->fail("Expecting an exception, none occurred"); 4338 } catch (moodle_exception $e) { 4339 $this->assertInstanceOf('coding_exception', $e); 4340 } catch (Error $error) { 4341 // PHP 7.1 throws Error even earlier. 4342 $this->assertRegExp('/Too few arguments to function/', $error->getMessage()); 4343 } 4344 4345 // Cover the function using placeholders in all positions. 4346 $start = 4; 4347 $length = 2; 4348 // 1st param (target). 4349 $sql = "SELECT id, ".$DB->sql_substr(":param1", $start)." AS name FROM {{$tablename}}"; 4350 $record = $DB->get_record_sql($sql, array('param1' => $string)); 4351 $this->assertEquals(substr($string, $start - 1), $record->name); // PHP's substr is 0-based. 4352 // 2nd param (start). 4353 $sql = "SELECT id, ".$DB->sql_substr("name", ":param1")." AS name FROM {{$tablename}}"; 4354 $record = $DB->get_record_sql($sql, array('param1' => $start)); 4355 $this->assertEquals(substr($string, $start - 1), $record->name); // PHP's substr is 0-based. 4356 // 3rd param (length). 4357 $sql = "SELECT id, ".$DB->sql_substr("name", $start, ":param1")." AS name FROM {{$tablename}}"; 4358 $record = $DB->get_record_sql($sql, array('param1' => $length)); 4359 $this->assertEquals(substr($string, $start - 1, $length), $record->name); // PHP's substr is 0-based. 4360 // All together. 4361 $sql = "SELECT id, ".$DB->sql_substr(":param1", ":param2", ":param3")." AS name FROM {{$tablename}}"; 4362 $record = $DB->get_record_sql($sql, array('param1' => $string, 'param2' => $start, 'param3' => $length)); 4363 $this->assertEquals(substr($string, $start - 1, $length), $record->name); // PHP's substr is 0-based. 4364 4365 // Try also with some expression passed. 4366 $sql = "SELECT id, ".$DB->sql_substr("name", "(:param1 + 1) - 1")." AS name FROM {{$tablename}}"; 4367 $record = $DB->get_record_sql($sql, array('param1' => $start)); 4368 $this->assertEquals(substr($string, $start - 1), $record->name); // PHP's substr is 0-based. 4369 } 4370 4371 public function test_sql_length() { 4372 $DB = $this->tdb; 4373 $this->assertEquals($DB->get_field_sql( 4374 "SELECT ".$DB->sql_length("'aeiou'").$DB->sql_null_from_clause()), 5); 4375 $this->assertEquals($DB->get_field_sql( 4376 "SELECT ".$DB->sql_length("'áéíóú'").$DB->sql_null_from_clause()), 5); 4377 } 4378 4379 public function test_sql_position() { 4380 $DB = $this->tdb; 4381 $this->assertEquals($DB->get_field_sql( 4382 "SELECT ".$DB->sql_position("'ood'", "'Moodle'").$DB->sql_null_from_clause()), 2); 4383 $this->assertEquals($DB->get_field_sql( 4384 "SELECT ".$DB->sql_position("'Oracle'", "'Moodle'").$DB->sql_null_from_clause()), 0); 4385 } 4386 4387 public function test_sql_empty() { 4388 $DB = $this->tdb; 4389 $dbman = $DB->get_manager(); 4390 4391 $table = $this->get_test_table(); 4392 $tablename = $table->getName(); 4393 4394 $this->assertSame('', $DB->sql_empty()); // Since 2.5 the hack is applied automatically to all bound params. 4395 $this->assertDebuggingCalled(); 4396 4397 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4398 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 4399 $table->add_field('namenotnull', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'default value'); 4400 $table->add_field('namenotnullnodeflt', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); 4401 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4402 $dbman->create_table($table); 4403 4404 $DB->insert_record($tablename, array('name'=>'', 'namenotnull'=>'')); 4405 $DB->insert_record($tablename, array('name'=>null)); 4406 $DB->insert_record($tablename, array('name'=>'lalala')); 4407 $DB->insert_record($tablename, array('name'=>0)); 4408 4409 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array('')); 4410 $this->assertCount(1, $records); 4411 $record = reset($records); 4412 $this->assertSame('', $record->name); 4413 4414 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnull = ?", array('')); 4415 $this->assertCount(1, $records); 4416 $record = reset($records); 4417 $this->assertSame('', $record->namenotnull); 4418 4419 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnullnodeflt = ?", array('')); 4420 $this->assertCount(4, $records); 4421 $record = reset($records); 4422 $this->assertSame('', $record->namenotnullnodeflt); 4423 } 4424 4425 public function test_sql_isempty() { 4426 $DB = $this->tdb; 4427 $dbman = $DB->get_manager(); 4428 4429 $table = $this->get_test_table(); 4430 $tablename = $table->getName(); 4431 4432 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4433 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); 4434 $table->add_field('namenull', XMLDB_TYPE_CHAR, '255', null, null, null, null); 4435 $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null); 4436 $table->add_field('descriptionnull', XMLDB_TYPE_TEXT, 'big', null, null, null, null); 4437 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4438 $dbman->create_table($table); 4439 4440 $DB->insert_record($tablename, array('name'=>'', 'namenull'=>'', 'description'=>'', 'descriptionnull'=>'')); 4441 $DB->insert_record($tablename, array('name'=>'??', 'namenull'=>null, 'description'=>'??', 'descriptionnull'=>null)); 4442 $DB->insert_record($tablename, array('name'=>'la', 'namenull'=>'la', 'description'=>'la', 'descriptionnull'=>'lalala')); 4443 $DB->insert_record($tablename, array('name'=>0, 'namenull'=>0, 'description'=>0, 'descriptionnull'=>0)); 4444 4445 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'name', false, false)); 4446 $this->assertCount(1, $records); 4447 $record = reset($records); 4448 $this->assertSame('', $record->name); 4449 4450 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'namenull', true, false)); 4451 $this->assertCount(1, $records); 4452 $record = reset($records); 4453 $this->assertSame('', $record->namenull); 4454 4455 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'description', false, true)); 4456 $this->assertCount(1, $records); 4457 $record = reset($records); 4458 $this->assertSame('', $record->description); 4459 4460 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'descriptionnull', true, true)); 4461 $this->assertCount(1, $records); 4462 $record = reset($records); 4463 $this->assertSame('', $record->descriptionnull); 4464 } 4465 4466 public function test_sql_isnotempty() { 4467 $DB = $this->tdb; 4468 $dbman = $DB->get_manager(); 4469 4470 $table = $this->get_test_table(); 4471 $tablename = $table->getName(); 4472 4473 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4474 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); 4475 $table->add_field('namenull', XMLDB_TYPE_CHAR, '255', null, null, null, null); 4476 $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null); 4477 $table->add_field('descriptionnull', XMLDB_TYPE_TEXT, 'big', null, null, null, null); 4478 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4479 $dbman->create_table($table); 4480 4481 $DB->insert_record($tablename, array('name'=>'', 'namenull'=>'', 'description'=>'', 'descriptionnull'=>'')); 4482 $DB->insert_record($tablename, array('name'=>'??', 'namenull'=>null, 'description'=>'??', 'descriptionnull'=>null)); 4483 $DB->insert_record($tablename, array('name'=>'la', 'namenull'=>'la', 'description'=>'la', 'descriptionnull'=>'lalala')); 4484 $DB->insert_record($tablename, array('name'=>0, 'namenull'=>0, 'description'=>0, 'descriptionnull'=>0)); 4485 4486 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'name', false, false)); 4487 $this->assertCount(3, $records); 4488 $record = reset($records); 4489 $this->assertSame('??', $record->name); 4490 4491 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'namenull', true, false)); 4492 $this->assertCount(2, $records); // Nulls aren't comparable (so they aren't "not empty"). SQL expected behaviour. 4493 $record = reset($records); 4494 $this->assertSame('la', $record->namenull); // So 'la' is the first non-empty 'namenull' record. 4495 4496 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'description', false, true)); 4497 $this->assertCount(3, $records); 4498 $record = reset($records); 4499 $this->assertSame('??', $record->description); 4500 4501 $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'descriptionnull', true, true)); 4502 $this->assertCount(2, $records); // Nulls aren't comparable (so they aren't "not empty"). SQL expected behaviour. 4503 $record = reset($records); 4504 $this->assertSame('lalala', $record->descriptionnull); // So 'lalala' is the first non-empty 'descriptionnull' record. 4505 } 4506 4507 public function test_sql_regex() { 4508 $DB = $this->tdb; 4509 $dbman = $DB->get_manager(); 4510 if (!$DB->sql_regex_supported()) { 4511 $this->markTestSkipped($DB->get_name().' does not support regular expressions'); 4512 } 4513 4514 $table = $this->get_test_table(); 4515 $tablename = $table->getName(); 4516 4517 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4518 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 4519 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4520 $dbman->create_table($table); 4521 4522 $DB->insert_record($tablename, array('name'=>'LALALA')); 4523 $DB->insert_record($tablename, array('name'=>'holaaa')); 4524 $DB->insert_record($tablename, array('name'=>'aouch')); 4525 4526 // Regex /a$/i (case-insensitive). 4527 $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_regex()." ?"; 4528 $params = array('a$'); 4529 $records = $DB->get_records_sql($sql, $params); 4530 $this->assertCount(2, $records); 4531 4532 // Regex ! (not) /.a/i (case insensitive). 4533 $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_regex(false)." ?"; 4534 $params = array('.a'); 4535 $records = $DB->get_records_sql($sql, $params); 4536 $this->assertCount(1, $records); 4537 4538 // Regex /a$/ (case-sensitive). 4539 $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_regex(true, true)." ?"; 4540 $params = array('a$'); 4541 $records = $DB->get_records_sql($sql, $params); 4542 $this->assertCount(1, $records); 4543 4544 // Regex ! (not) /.a/ (case sensitive). 4545 $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_regex(false, true)." ?"; 4546 $params = array('.a'); 4547 $records = $DB->get_records_sql($sql, $params); 4548 $this->assertCount(2, $records); 4549 4550 } 4551 4552 /** 4553 * Test some complicated variations of set_field_select. 4554 */ 4555 public function test_set_field_select_complicated() { 4556 $DB = $this->tdb; 4557 $dbman = $DB->get_manager(); 4558 4559 $table = $this->get_test_table(); 4560 $tablename = $table->getName(); 4561 4562 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4563 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 4564 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 4565 $table->add_field('content', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL); 4566 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4567 $dbman->create_table($table); 4568 4569 $DB->insert_record($tablename, array('course' => 3, 'content' => 'hello', 'name'=>'xyz')); 4570 $DB->insert_record($tablename, array('course' => 3, 'content' => 'world', 'name'=>'abc')); 4571 $DB->insert_record($tablename, array('course' => 5, 'content' => 'hello', 'name'=>'def')); 4572 $DB->insert_record($tablename, array('course' => 2, 'content' => 'universe', 'name'=>'abc')); 4573 // This SQL is a tricky case because we are selecting from the same table we are updating. 4574 $sql = 'id IN (SELECT outerq.id from (SELECT innerq.id from {' . $tablename . '} innerq WHERE course = 3) outerq)'; 4575 $DB->set_field_select($tablename, 'name', 'ghi', $sql); 4576 4577 $this->assertSame(2, $DB->count_records_select($tablename, 'name = ?', array('ghi'))); 4578 4579 } 4580 4581 /** 4582 * Test some more complex SQL syntax which moodle uses and depends on to work 4583 * useful to determine if new database libraries can be supported. 4584 */ 4585 public function test_get_records_sql_complicated() { 4586 $DB = $this->tdb; 4587 $dbman = $DB->get_manager(); 4588 4589 $table = $this->get_test_table(); 4590 $tablename = $table->getName(); 4591 4592 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4593 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 4594 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 4595 $table->add_field('content', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL); 4596 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4597 $dbman->create_table($table); 4598 4599 $DB->insert_record($tablename, array('course' => 3, 'content' => 'hello', 'name'=>'xyz')); 4600 $DB->insert_record($tablename, array('course' => 3, 'content' => 'world', 'name'=>'abc')); 4601 $DB->insert_record($tablename, array('course' => 5, 'content' => 'hello', 'name'=>'def')); 4602 $DB->insert_record($tablename, array('course' => 2, 'content' => 'universe', 'name'=>'abc')); 4603 4604 // Test grouping by expressions in the query. MDL-26819. Note that there are 4 ways: 4605 // - By column position (GROUP by 1) - Not supported by mssql & oracle 4606 // - By column name (GROUP by course) - Supported by all, but leading to wrong results 4607 // - By column alias (GROUP by casecol) - Not supported by mssql & oracle 4608 // - By complete expression (GROUP BY CASE ...) - 100% cross-db, this test checks it 4609 $sql = "SELECT (CASE WHEN course = 3 THEN 1 ELSE 0 END) AS casecol, 4610 COUNT(1) AS countrecs, 4611 MAX(name) AS maxname 4612 FROM {{$tablename}} 4613 GROUP BY CASE WHEN course = 3 THEN 1 ELSE 0 END 4614 ORDER BY casecol DESC"; 4615 $result = array( 4616 1 => (object)array('casecol' => 1, 'countrecs' => 2, 'maxname' => 'xyz'), 4617 0 => (object)array('casecol' => 0, 'countrecs' => 2, 'maxname' => 'def')); 4618 $records = $DB->get_records_sql($sql, null); 4619 $this->assertEquals($result, $records); 4620 4621 // Another grouping by CASE expression just to ensure it works ok for multiple WHEN. 4622 $sql = "SELECT CASE name 4623 WHEN 'xyz' THEN 'last' 4624 WHEN 'def' THEN 'mid' 4625 WHEN 'abc' THEN 'first' 4626 END AS casecol, 4627 COUNT(1) AS countrecs, 4628 MAX(name) AS maxname 4629 FROM {{$tablename}} 4630 GROUP BY CASE name 4631 WHEN 'xyz' THEN 'last' 4632 WHEN 'def' THEN 'mid' 4633 WHEN 'abc' THEN 'first' 4634 END 4635 ORDER BY casecol DESC"; 4636 $result = array( 4637 'mid' => (object)array('casecol' => 'mid', 'countrecs' => 1, 'maxname' => 'def'), 4638 'last' => (object)array('casecol' => 'last', 'countrecs' => 1, 'maxname' => 'xyz'), 4639 'first'=> (object)array('casecol' => 'first', 'countrecs' => 2, 'maxname' => 'abc')); 4640 $records = $DB->get_records_sql($sql, null); 4641 $this->assertEquals($result, $records); 4642 4643 // Test CASE expressions in the ORDER BY clause - used by MDL-34657. 4644 $sql = "SELECT id, course, name 4645 FROM {{$tablename}} 4646 ORDER BY CASE WHEN (course = 5 OR name = 'xyz') THEN 0 ELSE 1 END, name, course"; 4647 // First, records matching the course = 5 OR name = 'xyz', then the rest. Each. 4648 // group ordered by name and course. 4649 $result = array( 4650 3 => (object)array('id' => 3, 'course' => 5, 'name' => 'def'), 4651 1 => (object)array('id' => 1, 'course' => 3, 'name' => 'xyz'), 4652 4 => (object)array('id' => 4, 'course' => 2, 'name' => 'abc'), 4653 2 => (object)array('id' => 2, 'course' => 3, 'name' => 'abc')); 4654 $records = $DB->get_records_sql($sql, null); 4655 $this->assertEquals($result, $records); 4656 // Verify also array keys, order is important in this test. 4657 $this->assertEquals(array_keys($result), array_keys($records)); 4658 4659 // Test limits in queries with DISTINCT/ALL clauses and multiple whitespace. MDL-25268. 4660 $sql = "SELECT DISTINCT course 4661 FROM {{$tablename}} 4662 ORDER BY course"; 4663 // Only limitfrom. 4664 $records = $DB->get_records_sql($sql, null, 1); 4665 $this->assertCount(2, $records); 4666 $this->assertEquals(3, reset($records)->course); 4667 $this->assertEquals(5, next($records)->course); 4668 // Only limitnum. 4669 $records = $DB->get_records_sql($sql, null, 0, 2); 4670 $this->assertCount(2, $records); 4671 $this->assertEquals(2, reset($records)->course); 4672 $this->assertEquals(3, next($records)->course); 4673 // Both limitfrom and limitnum. 4674 $records = $DB->get_records_sql($sql, null, 2, 2); 4675 $this->assertCount(1, $records); 4676 $this->assertEquals(5, reset($records)->course); 4677 4678 // We have sql like this in moodle, this syntax breaks on older versions of sqlite for example.. 4679 $sql = "SELECT a.id AS id, a.course AS course 4680 FROM {{$tablename}} a 4681 JOIN (SELECT * FROM {{$tablename}}) b ON a.id = b.id 4682 WHERE a.course = ?"; 4683 4684 $records = $DB->get_records_sql($sql, array(3)); 4685 $this->assertCount(2, $records); 4686 $this->assertEquals(1, reset($records)->id); 4687 $this->assertEquals(2, next($records)->id); 4688 4689 // Do NOT try embedding sql_xxxx() helper functions in conditions array of count_records(), they don't break params/binding! 4690 $count = $DB->count_records_select($tablename, "course = :course AND ".$DB->sql_compare_text('content')." = :content", array('course' => 3, 'content' => 'hello')); 4691 $this->assertEquals(1, $count); 4692 4693 // Test int x string comparison. 4694 $sql = "SELECT * 4695 FROM {{$tablename}} c 4696 WHERE name = ?"; 4697 $this->assertCount(0, $DB->get_records_sql($sql, array(10))); 4698 $this->assertCount(0, $DB->get_records_sql($sql, array("10"))); 4699 $DB->insert_record($tablename, array('course' => 7, 'content' => 'xx', 'name'=>'1')); 4700 $DB->insert_record($tablename, array('course' => 7, 'content' => 'yy', 'name'=>'2')); 4701 $this->assertCount(1, $DB->get_records_sql($sql, array(1))); 4702 $this->assertCount(1, $DB->get_records_sql($sql, array("1"))); 4703 $this->assertCount(0, $DB->get_records_sql($sql, array(10))); 4704 $this->assertCount(0, $DB->get_records_sql($sql, array("10"))); 4705 $DB->insert_record($tablename, array('course' => 7, 'content' => 'xx', 'name'=>'1abc')); 4706 $this->assertCount(1, $DB->get_records_sql($sql, array(1))); 4707 $this->assertCount(1, $DB->get_records_sql($sql, array("1"))); 4708 4709 // Test get_in_or_equal() with a big number of elements. Note that ideally 4710 // we should be detecting and warning about any use over, say, 200 elements 4711 // And recommend to change code to use subqueries and/or chunks instead. 4712 $currentcount = $DB->count_records($tablename); 4713 $numelements = 10000; // Verify that we can handle 10000 elements (crazy!) 4714 $values = range(1, $numelements); 4715 4716 list($insql, $inparams) = $DB->get_in_or_equal($values, SQL_PARAMS_QM); // With QM params. 4717 $sql = "SELECT * 4718 FROM {{$tablename}} 4719 WHERE id $insql"; 4720 $results = $DB->get_records_sql($sql, $inparams); 4721 $this->assertCount($currentcount, $results); 4722 4723 list($insql, $inparams) = $DB->get_in_or_equal($values, SQL_PARAMS_NAMED); // With NAMED params. 4724 $sql = "SELECT * 4725 FROM {{$tablename}} 4726 WHERE id $insql"; 4727 $results = $DB->get_records_sql($sql, $inparams); 4728 $this->assertCount($currentcount, $results); 4729 } 4730 4731 public function test_replace_all_text() { 4732 $DB = $this->tdb; 4733 $dbman = $DB->get_manager(); 4734 4735 if (!$DB->replace_all_text_supported()) { 4736 $this->markTestSkipped($DB->get_name().' does not support replacing of texts'); 4737 } 4738 4739 $table = $this->get_test_table(); 4740 $tablename = $table->getName(); 4741 4742 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4743 $table->add_field('name', XMLDB_TYPE_CHAR, '20', null, null); 4744 $table->add_field('intro', XMLDB_TYPE_TEXT, 'big', null, null); 4745 // Add a CHAR field named using a word reserved for all the supported DB servers. 4746 $table->add_field('where', XMLDB_TYPE_CHAR, '20', null, null, null, 'localhost'); 4747 // Add a TEXT field named using a word reserved for all the supported DB servers. 4748 $table->add_field('from', XMLDB_TYPE_TEXT, 'big', null, null); 4749 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4750 $dbman->create_table($table); 4751 4752 $fromfield = $dbman->generator->getEncQuoted('from'); 4753 $DB->execute("INSERT INTO {".$tablename."} (name,intro,$fromfield) VALUES (NULL,NULL,'localhost')"); 4754 $DB->execute("INSERT INTO {".$tablename."} (name,intro,$fromfield) VALUES ('','','localhost')"); 4755 $DB->execute("INSERT INTO {".$tablename."} (name,intro,$fromfield) VALUES ('xxyy','vvzz','localhost')"); 4756 $DB->execute("INSERT INTO {".$tablename."} (name,intro,$fromfield) VALUES ('aa bb aa bb','cc dd cc aa','localhost')"); 4757 $DB->execute("INSERT INTO {".$tablename."} (name,intro,$fromfield) VALUES ('kkllll','kkllll','localhost')"); 4758 4759 $expected = $DB->get_records($tablename, array(), 'id ASC'); 4760 $idx = 1; 4761 $id1 = $id2 = $id3 = $id4 = $id5 = 0; 4762 foreach (array_keys($expected) as $identifier) { 4763 ${"id$idx"} = (string)$identifier; 4764 $idx++; 4765 } 4766 4767 $columns = $DB->get_columns($tablename); 4768 4769 // Replace should work even with columns named using a reserved word. 4770 $this->assertEquals('C', $columns['where']->meta_type); 4771 $this->assertEquals('localhost', $expected[$id1]->where); 4772 $this->assertEquals('localhost', $expected[$id2]->where); 4773 $this->assertEquals('localhost', $expected[$id3]->where); 4774 $this->assertEquals('localhost', $expected[$id4]->where); 4775 $this->assertEquals('localhost', $expected[$id5]->where); 4776 $DB->replace_all_text($tablename, $columns['where'], 'localhost', '::1'); 4777 $result = $DB->get_records($tablename, array(), 'id ASC'); 4778 $expected[$id1]->where = '::1'; 4779 $expected[$id2]->where = '::1'; 4780 $expected[$id3]->where = '::1'; 4781 $expected[$id4]->where = '::1'; 4782 $expected[$id5]->where = '::1'; 4783 $this->assertEquals($expected, $result); 4784 $this->assertEquals('X', $columns['from']->meta_type); 4785 $DB->replace_all_text($tablename, $columns['from'], 'localhost', '127.0.0.1'); 4786 $result = $DB->get_records($tablename, array(), 'id ASC'); 4787 $expected[$id1]->from = '127.0.0.1'; 4788 $expected[$id2]->from = '127.0.0.1'; 4789 $expected[$id3]->from = '127.0.0.1'; 4790 $expected[$id4]->from = '127.0.0.1'; 4791 $expected[$id5]->from = '127.0.0.1'; 4792 $this->assertEquals($expected, $result); 4793 4794 $DB->replace_all_text($tablename, $columns['name'], 'aa', 'o'); 4795 $result = $DB->get_records($tablename, array(), 'id ASC'); 4796 $expected[$id4]->name = 'o bb o bb'; 4797 $this->assertEquals($expected, $result); 4798 4799 $DB->replace_all_text($tablename, $columns['intro'], 'aa', 'o'); 4800 $result = $DB->get_records($tablename, array(), 'id ASC'); 4801 $expected[$id4]->intro = 'cc dd cc o'; 4802 $this->assertEquals($expected, $result); 4803 4804 $DB->replace_all_text($tablename, $columns['name'], '_', '*'); 4805 $DB->replace_all_text($tablename, $columns['name'], '?', '*'); 4806 $DB->replace_all_text($tablename, $columns['name'], '%', '*'); 4807 $DB->replace_all_text($tablename, $columns['intro'], '_', '*'); 4808 $DB->replace_all_text($tablename, $columns['intro'], '?', '*'); 4809 $DB->replace_all_text($tablename, $columns['intro'], '%', '*'); 4810 $result = $DB->get_records($tablename, array(), 'id ASC'); 4811 $this->assertEquals($expected, $result); 4812 4813 $long = '1234567890123456789'; 4814 $DB->replace_all_text($tablename, $columns['name'], 'kk', $long); 4815 $result = $DB->get_records($tablename, array(), 'id ASC'); 4816 $expected[$id5]->name = core_text::substr($long.'llll', 0, 20); 4817 $this->assertEquals($expected, $result); 4818 4819 $DB->replace_all_text($tablename, $columns['intro'], 'kk', $long); 4820 $result = $DB->get_records($tablename, array(), 'id ASC'); 4821 $expected[$id5]->intro = $long.'llll'; 4822 $this->assertEquals($expected, $result); 4823 } 4824 4825 public function test_onelevel_commit() { 4826 $DB = $this->tdb; 4827 $dbman = $DB->get_manager(); 4828 4829 $table = $this->get_test_table(); 4830 $tablename = $table->getName(); 4831 4832 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4833 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 4834 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4835 $dbman->create_table($table); 4836 4837 $transaction = $DB->start_delegated_transaction(); 4838 $data = (object)array('course'=>3); 4839 $this->assertEquals(0, $DB->count_records($tablename)); 4840 $DB->insert_record($tablename, $data); 4841 $this->assertEquals(1, $DB->count_records($tablename)); 4842 $transaction->allow_commit(); 4843 $this->assertEquals(1, $DB->count_records($tablename)); 4844 } 4845 4846 public function test_transaction_ignore_error_trouble() { 4847 $DB = $this->tdb; 4848 $dbman = $DB->get_manager(); 4849 4850 $table = $this->get_test_table(); 4851 $tablename = $table->getName(); 4852 4853 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4854 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 4855 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4856 $table->add_index('course', XMLDB_INDEX_UNIQUE, array('course')); 4857 $dbman->create_table($table); 4858 4859 // Test error on SQL_QUERY_INSERT. 4860 $transaction = $DB->start_delegated_transaction(); 4861 $this->assertEquals(0, $DB->count_records($tablename)); 4862 $DB->insert_record($tablename, (object)array('course'=>1)); 4863 $this->assertEquals(1, $DB->count_records($tablename)); 4864 try { 4865 $DB->insert_record($tablename, (object)array('course'=>1)); 4866 } catch (Exception $e) { 4867 // This must be ignored and it must not roll back the whole transaction. 4868 } 4869 $DB->insert_record($tablename, (object)array('course'=>2)); 4870 $this->assertEquals(2, $DB->count_records($tablename)); 4871 $transaction->allow_commit(); 4872 $this->assertEquals(2, $DB->count_records($tablename)); 4873 $this->assertFalse($DB->is_transaction_started()); 4874 4875 // Test error on SQL_QUERY_SELECT. 4876 $DB->delete_records($tablename); 4877 $transaction = $DB->start_delegated_transaction(); 4878 $this->assertEquals(0, $DB->count_records($tablename)); 4879 $DB->insert_record($tablename, (object)array('course'=>1)); 4880 $this->assertEquals(1, $DB->count_records($tablename)); 4881 try { 4882 $DB->get_records_sql('s e l e c t'); 4883 } catch (moodle_exception $e) { 4884 // This must be ignored and it must not roll back the whole transaction. 4885 } 4886 $DB->insert_record($tablename, (object)array('course'=>2)); 4887 $this->assertEquals(2, $DB->count_records($tablename)); 4888 $transaction->allow_commit(); 4889 $this->assertEquals(2, $DB->count_records($tablename)); 4890 $this->assertFalse($DB->is_transaction_started()); 4891 4892 // Test error on structure SQL_QUERY_UPDATE. 4893 $DB->delete_records($tablename); 4894 $transaction = $DB->start_delegated_transaction(); 4895 $this->assertEquals(0, $DB->count_records($tablename)); 4896 $DB->insert_record($tablename, (object)array('course'=>1)); 4897 $this->assertEquals(1, $DB->count_records($tablename)); 4898 try { 4899 $DB->execute('xxxx'); 4900 } catch (moodle_exception $e) { 4901 // This must be ignored and it must not roll back the whole transaction. 4902 } 4903 $DB->insert_record($tablename, (object)array('course'=>2)); 4904 $this->assertEquals(2, $DB->count_records($tablename)); 4905 $transaction->allow_commit(); 4906 $this->assertEquals(2, $DB->count_records($tablename)); 4907 $this->assertFalse($DB->is_transaction_started()); 4908 4909 // Test error on structure SQL_QUERY_STRUCTURE. 4910 $DB->delete_records($tablename); 4911 $transaction = $DB->start_delegated_transaction(); 4912 $this->assertEquals(0, $DB->count_records($tablename)); 4913 $DB->insert_record($tablename, (object)array('course'=>1)); 4914 $this->assertEquals(1, $DB->count_records($tablename)); 4915 try { 4916 $DB->change_database_structure('xxxx'); 4917 } catch (moodle_exception $e) { 4918 // This must be ignored and it must not roll back the whole transaction. 4919 } 4920 $DB->insert_record($tablename, (object)array('course'=>2)); 4921 $this->assertEquals(2, $DB->count_records($tablename)); 4922 $transaction->allow_commit(); 4923 $this->assertEquals(2, $DB->count_records($tablename)); 4924 $this->assertFalse($DB->is_transaction_started()); 4925 4926 // NOTE: SQL_QUERY_STRUCTURE is intentionally not tested here because it should never fail. 4927 } 4928 4929 public function test_onelevel_rollback() { 4930 $DB = $this->tdb; 4931 $dbman = $DB->get_manager(); 4932 4933 $table = $this->get_test_table(); 4934 $tablename = $table->getName(); 4935 4936 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4937 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 4938 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4939 $dbman->create_table($table); 4940 4941 // This might in fact encourage ppl to migrate from myisam to innodb. 4942 4943 $transaction = $DB->start_delegated_transaction(); 4944 $data = (object)array('course'=>3); 4945 $this->assertEquals(0, $DB->count_records($tablename)); 4946 $DB->insert_record($tablename, $data); 4947 $this->assertEquals(1, $DB->count_records($tablename)); 4948 try { 4949 $transaction->rollback(new Exception('test')); 4950 $this->fail('transaction rollback must rethrow exception'); 4951 } catch (Exception $e) { 4952 // Ignored. 4953 } 4954 $this->assertEquals(0, $DB->count_records($tablename)); 4955 } 4956 4957 public function test_nested_transactions() { 4958 $DB = $this->tdb; 4959 $dbman = $DB->get_manager(); 4960 4961 $table = $this->get_test_table(); 4962 $tablename = $table->getName(); 4963 4964 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 4965 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 4966 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 4967 $dbman->create_table($table); 4968 4969 // Two level commit. 4970 $this->assertFalse($DB->is_transaction_started()); 4971 $transaction1 = $DB->start_delegated_transaction(); 4972 $this->assertTrue($DB->is_transaction_started()); 4973 $data = (object)array('course'=>3); 4974 $DB->insert_record($tablename, $data); 4975 $transaction2 = $DB->start_delegated_transaction(); 4976 $data = (object)array('course'=>4); 4977 $DB->insert_record($tablename, $data); 4978 $transaction2->allow_commit(); 4979 $this->assertTrue($DB->is_transaction_started()); 4980 $transaction1->allow_commit(); 4981 $this->assertFalse($DB->is_transaction_started()); 4982 $this->assertEquals(2, $DB->count_records($tablename)); 4983 4984 $DB->delete_records($tablename); 4985 4986 // Rollback from top level. 4987 $transaction1 = $DB->start_delegated_transaction(); 4988 $data = (object)array('course'=>3); 4989 $DB->insert_record($tablename, $data); 4990 $transaction2 = $DB->start_delegated_transaction(); 4991 $data = (object)array('course'=>4); 4992 $DB->insert_record($tablename, $data); 4993 $transaction2->allow_commit(); 4994 try { 4995 $transaction1->rollback(new Exception('test')); 4996 $this->fail('transaction rollback must rethrow exception'); 4997 } catch (Exception $e) { 4998 $this->assertEquals(get_class($e), 'Exception'); 4999 } 5000 $this->assertEquals(0, $DB->count_records($tablename)); 5001 5002 $DB->delete_records($tablename); 5003 5004 // Rollback from nested level. 5005 $transaction1 = $DB->start_delegated_transaction(); 5006 $data = (object)array('course'=>3); 5007 $DB->insert_record($tablename, $data); 5008 $transaction2 = $DB->start_delegated_transaction(); 5009 $data = (object)array('course'=>4); 5010 $DB->insert_record($tablename, $data); 5011 try { 5012 $transaction2->rollback(new Exception('test')); 5013 $this->fail('transaction rollback must rethrow exception'); 5014 } catch (Exception $e) { 5015 $this->assertEquals(get_class($e), 'Exception'); 5016 } 5017 $this->assertEquals(2, $DB->count_records($tablename)); // Not rolled back yet. 5018 try { 5019 $transaction1->allow_commit(); 5020 } catch (moodle_exception $e) { 5021 $this->assertInstanceOf('dml_transaction_exception', $e); 5022 } 5023 $this->assertEquals(2, $DB->count_records($tablename)); // Not rolled back yet. 5024 // The forced rollback is done from the default_exception handler and similar places, 5025 // let's do it manually here. 5026 $this->assertTrue($DB->is_transaction_started()); 5027 $DB->force_transaction_rollback(); 5028 $this->assertFalse($DB->is_transaction_started()); 5029 $this->assertEquals(0, $DB->count_records($tablename)); // Finally rolled back. 5030 5031 $DB->delete_records($tablename); 5032 5033 // Test interactions of recordset and transactions - this causes problems in SQL Server. 5034 $table2 = $this->get_test_table('2'); 5035 $tablename2 = $table2->getName(); 5036 5037 $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 5038 $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 5039 $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 5040 $dbman->create_table($table2); 5041 5042 $DB->insert_record($tablename, array('course'=>1)); 5043 $DB->insert_record($tablename, array('course'=>2)); 5044 $DB->insert_record($tablename, array('course'=>3)); 5045 5046 $DB->insert_record($tablename2, array('course'=>5)); 5047 $DB->insert_record($tablename2, array('course'=>6)); 5048 $DB->insert_record($tablename2, array('course'=>7)); 5049 $DB->insert_record($tablename2, array('course'=>8)); 5050 5051 $rs1 = $DB->get_recordset($tablename); 5052 $i = 0; 5053 foreach ($rs1 as $record1) { 5054 $i++; 5055 $rs2 = $DB->get_recordset($tablename2); 5056 $j = 0; 5057 foreach ($rs2 as $record2) { 5058 $t = $DB->start_delegated_transaction(); 5059 $DB->set_field($tablename, 'course', $record1->course+1, array('id'=>$record1->id)); 5060 $DB->set_field($tablename2, 'course', $record2->course+1, array('id'=>$record2->id)); 5061 $t->allow_commit(); 5062 $j++; 5063 } 5064 $rs2->close(); 5065 $this->assertEquals(4, $j); 5066 } 5067 $rs1->close(); 5068 $this->assertEquals(3, $i); 5069 5070 // Test nested recordsets isolation without transaction. 5071 $DB->delete_records($tablename); 5072 $DB->insert_record($tablename, array('course'=>1)); 5073 $DB->insert_record($tablename, array('course'=>2)); 5074 $DB->insert_record($tablename, array('course'=>3)); 5075 5076 $DB->delete_records($tablename2); 5077 $DB->insert_record($tablename2, array('course'=>5)); 5078 $DB->insert_record($tablename2, array('course'=>6)); 5079 $DB->insert_record($tablename2, array('course'=>7)); 5080 $DB->insert_record($tablename2, array('course'=>8)); 5081 5082 $rs1 = $DB->get_recordset($tablename); 5083 $i = 0; 5084 foreach ($rs1 as $record1) { 5085 $i++; 5086 $rs2 = $DB->get_recordset($tablename2); 5087 $j = 0; 5088 foreach ($rs2 as $record2) { 5089 $DB->set_field($tablename, 'course', $record1->course+1, array('id'=>$record1->id)); 5090 $DB->set_field($tablename2, 'course', $record2->course+1, array('id'=>$record2->id)); 5091 $j++; 5092 } 5093 $rs2->close(); 5094 $this->assertEquals(4, $j); 5095 } 5096 $rs1->close(); 5097 $this->assertEquals(3, $i); 5098 } 5099 5100 public function test_transactions_forbidden() { 5101 $DB = $this->tdb; 5102 $dbman = $DB->get_manager(); 5103 5104 $table = $this->get_test_table(); 5105 $tablename = $table->getName(); 5106 5107 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 5108 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 5109 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 5110 $dbman->create_table($table); 5111 5112 $DB->transactions_forbidden(); 5113 $transaction = $DB->start_delegated_transaction(); 5114 $data = (object)array('course'=>1); 5115 $DB->insert_record($tablename, $data); 5116 try { 5117 $DB->transactions_forbidden(); 5118 } catch (moodle_exception $e) { 5119 $this->assertInstanceOf('dml_transaction_exception', $e); 5120 } 5121 // The previous test does not force rollback. 5122 $transaction->allow_commit(); 5123 $this->assertFalse($DB->is_transaction_started()); 5124 $this->assertEquals(1, $DB->count_records($tablename)); 5125 } 5126 5127 public function test_wrong_transactions() { 5128 $DB = $this->tdb; 5129 $dbman = $DB->get_manager(); 5130 5131 $table = $this->get_test_table(); 5132 $tablename = $table->getName(); 5133 5134 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 5135 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 5136 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 5137 $dbman->create_table($table); 5138 5139 // Wrong order of nested commits. 5140 $transaction1 = $DB->start_delegated_transaction(); 5141 $data = (object)array('course'=>3); 5142 $DB->insert_record($tablename, $data); 5143 $transaction2 = $DB->start_delegated_transaction(); 5144 $data = (object)array('course'=>4); 5145 $DB->insert_record($tablename, $data); 5146 try { 5147 $transaction1->allow_commit(); 5148 $this->fail('wrong order of commits must throw exception'); 5149 } catch (moodle_exception $e) { 5150 $this->assertInstanceOf('dml_transaction_exception', $e); 5151 } 5152 try { 5153 $transaction2->allow_commit(); 5154 $this->fail('first wrong commit forces rollback'); 5155 } catch (moodle_exception $e) { 5156 $this->assertInstanceOf('dml_transaction_exception', $e); 5157 } 5158 // This is done in default exception handler usually. 5159 $this->assertTrue($DB->is_transaction_started()); 5160 $this->assertEquals(2, $DB->count_records($tablename)); // Not rolled back yet. 5161 $DB->force_transaction_rollback(); 5162 $this->assertEquals(0, $DB->count_records($tablename)); 5163 $DB->delete_records($tablename); 5164 5165 // Wrong order of nested rollbacks. 5166 $transaction1 = $DB->start_delegated_transaction(); 5167 $data = (object)array('course'=>3); 5168 $DB->insert_record($tablename, $data); 5169 $transaction2 = $DB->start_delegated_transaction(); 5170 $data = (object)array('course'=>4); 5171 $DB->insert_record($tablename, $data); 5172 try { 5173 // This first rollback should prevent all other rollbacks. 5174 $transaction1->rollback(new Exception('test')); 5175 } catch (Exception $e) { 5176 $this->assertEquals(get_class($e), 'Exception'); 5177 } 5178 try { 5179 $transaction2->rollback(new Exception('test')); 5180 } catch (Exception $e) { 5181 $this->assertEquals(get_class($e), 'Exception'); 5182 } 5183 try { 5184 $transaction1->rollback(new Exception('test')); 5185 } catch (moodle_exception $e) { 5186 $this->assertInstanceOf('dml_transaction_exception', $e); 5187 } 5188 // This is done in default exception handler usually. 5189 $this->assertTrue($DB->is_transaction_started()); 5190 $DB->force_transaction_rollback(); 5191 $DB->delete_records($tablename); 5192 5193 // Unknown transaction object. 5194 $transaction1 = $DB->start_delegated_transaction(); 5195 $data = (object)array('course'=>3); 5196 $DB->insert_record($tablename, $data); 5197 $transaction2 = new moodle_transaction($DB); 5198 try { 5199 $transaction2->allow_commit(); 5200 $this->fail('foreign transaction must fail'); 5201 } catch (moodle_exception $e) { 5202 $this->assertInstanceOf('dml_transaction_exception', $e); 5203 } 5204 try { 5205 $transaction1->allow_commit(); 5206 $this->fail('first wrong commit forces rollback'); 5207 } catch (moodle_exception $e) { 5208 $this->assertInstanceOf('dml_transaction_exception', $e); 5209 } 5210 $DB->force_transaction_rollback(); 5211 $DB->delete_records($tablename); 5212 } 5213 5214 public function test_concurent_transactions() { 5215 // Notes about this test: 5216 // 1- MySQL needs to use one engine with transactions support (InnoDB). 5217 // 2- MSSQL needs to have enabled versioning for read committed 5218 // transactions (ALTER DATABASE xxx SET READ_COMMITTED_SNAPSHOT ON) 5219 $DB = $this->tdb; 5220 $dbman = $DB->get_manager(); 5221 5222 $table = $this->get_test_table(); 5223 $tablename = $table->getName(); 5224 5225 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 5226 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 5227 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 5228 $dbman->create_table($table); 5229 5230 $transaction = $DB->start_delegated_transaction(); 5231 $data = (object)array('course'=>1); 5232 $this->assertEquals(0, $DB->count_records($tablename)); 5233 $DB->insert_record($tablename, $data); 5234 $this->assertEquals(1, $DB->count_records($tablename)); 5235 5236 // Open second connection. 5237 $cfg = $DB->export_dbconfig(); 5238 if (!isset($cfg->dboptions)) { 5239 $cfg->dboptions = array(); 5240 } 5241 // If we have a readonly slave situation, we need to either observe 5242 // the latency, or if the latency is not specified we need to take 5243 // the slave out because the table may not have propagated yet. 5244 if (isset($cfg->dboptions['readonly'])) { 5245 if (isset($cfg->dboptions['readonly']['latency'])) { 5246 usleep(intval(1000000 * $cfg->dboptions['readonly']['latency'])); 5247 } else { 5248 unset($cfg->dboptions['readonly']); 5249 } 5250 } 5251 $DB2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary); 5252 $DB2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions); 5253 5254 // Second instance should not see pending inserts. 5255 $this->assertEquals(0, $DB2->count_records($tablename)); 5256 $data = (object)array('course'=>2); 5257 $DB2->insert_record($tablename, $data); 5258 $this->assertEquals(1, $DB2->count_records($tablename)); 5259 5260 // First should see the changes done from second. 5261 $this->assertEquals(2, $DB->count_records($tablename)); 5262 5263 // Now commit and we should see it finally in second connections. 5264 $transaction->allow_commit(); 5265 $this->assertEquals(2, $DB2->count_records($tablename)); 5266 5267 // Let's try delete all is also working on (this checks MDL-29198). 5268 // Initially both connections see all the records in the table (2). 5269 $this->assertEquals(2, $DB->count_records($tablename)); 5270 $this->assertEquals(2, $DB2->count_records($tablename)); 5271 $transaction = $DB->start_delegated_transaction(); 5272 5273 // Delete all from within transaction. 5274 $DB->delete_records($tablename); 5275 5276 // Transactional $DB, sees 0 records now. 5277 $this->assertEquals(0, $DB->count_records($tablename)); 5278 5279 // Others ($DB2) get no changes yet. 5280 $this->assertEquals(2, $DB2->count_records($tablename)); 5281 5282 // Now commit and we should see changes. 5283 $transaction->allow_commit(); 5284 $this->assertEquals(0, $DB2->count_records($tablename)); 5285 5286 $DB2->dispose(); 5287 } 5288 5289 public function test_session_locks() { 5290 $DB = $this->tdb; 5291 $dbman = $DB->get_manager(); 5292 5293 // Open second connection. 5294 $cfg = $DB->export_dbconfig(); 5295 if (!isset($cfg->dboptions)) { 5296 $cfg->dboptions = array(); 5297 } 5298 $DB2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary); 5299 $DB2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions); 5300 5301 // Testing that acquiring a lock effectively locks. 5302 // Get a session lock on connection1. 5303 $rowid = rand(100, 200); 5304 $timeout = 1; 5305 $DB->get_session_lock($rowid, $timeout); 5306 5307 // Try to get the same session lock on connection2. 5308 try { 5309 $DB2->get_session_lock($rowid, $timeout); 5310 $DB2->release_session_lock($rowid); // Should not be executed, but here for safety. 5311 $this->fail('An Exception is missing, expected due to session lock acquired.'); 5312 } catch (moodle_exception $e) { 5313 $this->assertInstanceOf('dml_sessionwait_exception', $e); 5314 $DB->release_session_lock($rowid); // Release lock on connection1. 5315 } 5316 5317 // Testing that releasing a lock effectively frees. 5318 // Get a session lock on connection1. 5319 $rowid = rand(100, 200); 5320 $timeout = 1; 5321 $DB->get_session_lock($rowid, $timeout); 5322 // Release the lock on connection1. 5323 $DB->release_session_lock($rowid); 5324 5325 // Get the just released lock on connection2. 5326 $DB2->get_session_lock($rowid, $timeout); 5327 // Release the lock on connection2. 5328 $DB2->release_session_lock($rowid); 5329 5330 $DB2->dispose(); 5331 } 5332 5333 public function test_bound_param_types() { 5334 $DB = $this->tdb; 5335 $dbman = $DB->get_manager(); 5336 5337 $table = $this->get_test_table(); 5338 $tablename = $table->getName(); 5339 5340 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 5341 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 5342 $table->add_field('content', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL); 5343 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 5344 $dbman->create_table($table); 5345 5346 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => '1', 'content'=>'xx'))); 5347 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 2, 'content'=>'yy'))); 5348 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'somestring', 'content'=>'zz'))); 5349 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'aa', 'content'=>'1'))); 5350 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'bb', 'content'=>2))); 5351 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'cc', 'content'=>'sometext'))); 5352 5353 // Conditions in CHAR columns. 5354 $this->assertTrue($DB->record_exists($tablename, array('name'=>1))); 5355 $this->assertTrue($DB->record_exists($tablename, array('name'=>'1'))); 5356 $this->assertFalse($DB->record_exists($tablename, array('name'=>111))); 5357 $this->assertNotEmpty($DB->get_record($tablename, array('name'=>1))); 5358 $this->assertNotEmpty($DB->get_record($tablename, array('name'=>'1'))); 5359 $this->assertEmpty($DB->get_record($tablename, array('name'=>111))); 5360 $sqlqm = "SELECT * 5361 FROM {{$tablename}} 5362 WHERE name = ?"; 5363 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, array(1))); 5364 $this->assertCount(1, $records); 5365 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, array('1'))); 5366 $this->assertCount(1, $records); 5367 $records = $DB->get_records_sql($sqlqm, array(222)); 5368 $this->assertCount(0, $records); 5369 $sqlnamed = "SELECT * 5370 FROM {{$tablename}} 5371 WHERE name = :name"; 5372 $this->assertNotEmpty($records = $DB->get_records_sql($sqlnamed, array('name' => 2))); 5373 $this->assertCount(1, $records); 5374 $this->assertNotEmpty($records = $DB->get_records_sql($sqlnamed, array('name' => '2'))); 5375 $this->assertCount(1, $records); 5376 5377 // Conditions in TEXT columns always must be performed with the sql_compare_text 5378 // helper function on both sides of the condition. 5379 $sqlqm = "SELECT * 5380 FROM {{$tablename}} 5381 WHERE " . $DB->sql_compare_text('content') . " = " . $DB->sql_compare_text('?'); 5382 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, array('1'))); 5383 $this->assertCount(1, $records); 5384 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, array(1))); 5385 $this->assertCount(1, $records); 5386 $sqlnamed = "SELECT * 5387 FROM {{$tablename}} 5388 WHERE " . $DB->sql_compare_text('content') . " = " . $DB->sql_compare_text(':content'); 5389 $this->assertNotEmpty($records = $DB->get_records_sql($sqlnamed, array('content' => 2))); 5390 $this->assertCount(1, $records); 5391 $this->assertNotEmpty($records = $DB->get_records_sql($sqlnamed, array('content' => '2'))); 5392 $this->assertCount(1, $records); 5393 } 5394 5395 public function test_bound_param_reserved() { 5396 $DB = $this->tdb; 5397 $dbman = $DB->get_manager(); 5398 5399 $table = $this->get_test_table(); 5400 $tablename = $table->getName(); 5401 5402 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 5403 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 5404 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 5405 $dbman->create_table($table); 5406 5407 $DB->insert_record($tablename, array('course' => '1')); 5408 5409 // Make sure reserved words do not cause fatal problems in query parameters. 5410 5411 $DB->execute("UPDATE {{$tablename}} SET course = 1 WHERE id = :select", array('select'=>1)); 5412 $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE course = :select", array('select'=>1)); 5413 $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} WHERE course = :select", array('select'=>1)); 5414 $rs->close(); 5415 $DB->get_fieldset_sql("SELECT id FROM {{$tablename}} WHERE course = :select", array('select'=>1)); 5416 $DB->set_field_select($tablename, 'course', '1', "id = :select", array('select'=>1)); 5417 $DB->delete_records_select($tablename, "id = :select", array('select'=>1)); 5418 5419 // If we get here test passed ok. 5420 $this->assertTrue(true); 5421 } 5422 5423 public function test_limits_and_offsets() { 5424 $DB = $this->tdb; 5425 $dbman = $DB->get_manager(); 5426 5427 $table = $this->get_test_table(); 5428 $tablename = $table->getName(); 5429 5430 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 5431 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 5432 $table->add_field('content', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL); 5433 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 5434 $dbman->create_table($table); 5435 5436 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'a', 'content'=>'one'))); 5437 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'b', 'content'=>'two'))); 5438 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'c', 'content'=>'three'))); 5439 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'd', 'content'=>'four'))); 5440 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'e', 'content'=>'five'))); 5441 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'f', 'content'=>'six'))); 5442 5443 $sqlqm = "SELECT * 5444 FROM {{$tablename}}"; 5445 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 4)); 5446 $this->assertCount(2, $records); 5447 $this->assertSame('e', reset($records)->name); 5448 $this->assertSame('f', end($records)->name); 5449 5450 $sqlqm = "SELECT * 5451 FROM {{$tablename}}"; 5452 $this->assertEmpty($records = $DB->get_records_sql($sqlqm, null, 8)); 5453 5454 $sqlqm = "SELECT * 5455 FROM {{$tablename}}"; 5456 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 0, 4)); 5457 $this->assertCount(4, $records); 5458 $this->assertSame('a', reset($records)->name); 5459 $this->assertSame('d', end($records)->name); 5460 5461 $sqlqm = "SELECT * 5462 FROM {{$tablename}}"; 5463 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 0, 8)); 5464 $this->assertCount(6, $records); 5465 $this->assertSame('a', reset($records)->name); 5466 $this->assertSame('f', end($records)->name); 5467 5468 $sqlqm = "SELECT * 5469 FROM {{$tablename}}"; 5470 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 1, 4)); 5471 $this->assertCount(4, $records); 5472 $this->assertSame('b', reset($records)->name); 5473 $this->assertSame('e', end($records)->name); 5474 5475 $sqlqm = "SELECT * 5476 FROM {{$tablename}}"; 5477 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 4, 4)); 5478 $this->assertCount(2, $records); 5479 $this->assertSame('e', reset($records)->name); 5480 $this->assertSame('f', end($records)->name); 5481 5482 $sqlqm = "SELECT t.*, t.name AS test 5483 FROM {{$tablename}} t 5484 ORDER BY t.id ASC"; 5485 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 4, 4)); 5486 $this->assertCount(2, $records); 5487 $this->assertSame('e', reset($records)->name); 5488 $this->assertSame('f', end($records)->name); 5489 5490 $sqlqm = "SELECT DISTINCT t.name, t.name AS test 5491 FROM {{$tablename}} t 5492 ORDER BY t.name DESC"; 5493 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 4, 4)); 5494 $this->assertCount(2, $records); 5495 $this->assertSame('b', reset($records)->name); 5496 $this->assertSame('a', end($records)->name); 5497 5498 $sqlqm = "SELECT 1 5499 FROM {{$tablename}} t 5500 WHERE t.name = 'a'"; 5501 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 0, 1)); 5502 $this->assertCount(1, $records); 5503 5504 $sqlqm = "SELECT 'constant' 5505 FROM {{$tablename}} t 5506 WHERE t.name = 'a'"; 5507 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 0, 8)); 5508 $this->assertCount(1, $records); 5509 5510 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'a', 'content'=>'one'))); 5511 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'b', 'content'=>'two'))); 5512 $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'c', 'content'=>'three'))); 5513 5514 $sqlqm = "SELECT t.name, COUNT(DISTINCT t2.id) AS count, 'Test' AS teststring 5515 FROM {{$tablename}} t 5516 LEFT JOIN ( 5517 SELECT t.id, t.name 5518 FROM {{$tablename}} t 5519 ) t2 ON t2.name = t.name 5520 GROUP BY t.name 5521 ORDER BY t.name ASC"; 5522 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm)); 5523 $this->assertCount(6, $records); // a,b,c,d,e,f. 5524 $this->assertEquals(2, reset($records)->count); // a has 2 records now. 5525 $this->assertEquals(1, end($records)->count); // f has 1 record still. 5526 5527 $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 0, 2)); 5528 $this->assertCount(2, $records); 5529 $this->assertEquals(2, reset($records)->count); 5530 $this->assertEquals(2, end($records)->count); 5531 } 5532 5533 /** 5534 * Test debugging messages about invalid limit number values. 5535 */ 5536 public function test_invalid_limits_debugging() { 5537 $DB = $this->tdb; 5538 $dbman = $DB->get_manager(); 5539 5540 // Setup test data. 5541 $table = $this->get_test_table(); 5542 $tablename = $table->getName(); 5543 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 5544 $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 5545 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 5546 $dbman->create_table($table); 5547 $DB->insert_record($tablename, array('course' => '1')); 5548 5549 // Verify that get_records_sql throws debug notices with invalid limit params. 5550 $DB->get_records_sql("SELECT * FROM {{$tablename}}", null, 'invalid'); 5551 $this->assertDebuggingCalled("Non-numeric limitfrom parameter detected: 'invalid', did you pass the correct arguments?"); 5552 5553 $DB->get_records_sql("SELECT * FROM {{$tablename}}", null, 1, 'invalid'); 5554 $this->assertDebuggingCalled("Non-numeric limitnum parameter detected: 'invalid', did you pass the correct arguments?"); 5555 5556 // Verify that get_recordset_sql throws debug notices with invalid limit params. 5557 $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}}", null, 'invalid'); 5558 $this->assertDebuggingCalled("Non-numeric limitfrom parameter detected: 'invalid', did you pass the correct arguments?"); 5559 $rs->close(); 5560 5561 $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}}", null, 1, 'invalid'); 5562 $this->assertDebuggingCalled("Non-numeric limitnum parameter detected: 'invalid', did you pass the correct arguments?"); 5563 $rs->close(); 5564 5565 // Verify that some edge cases do no create debugging messages. 5566 // String form of integer values. 5567 $DB->get_records_sql("SELECT * FROM {{$tablename}}", null, '1'); 5568 $this->assertDebuggingNotCalled(); 5569 $DB->get_records_sql("SELECT * FROM {{$tablename}}", null, 1, '2'); 5570 $this->assertDebuggingNotCalled(); 5571 // Empty strings. 5572 $DB->get_records_sql("SELECT * FROM {{$tablename}}", null, ''); 5573 $this->assertDebuggingNotCalled(); 5574 $DB->get_records_sql("SELECT * FROM {{$tablename}}", null, 1, ''); 5575 $this->assertDebuggingNotCalled(); 5576 // Null values. 5577 $DB->get_records_sql("SELECT * FROM {{$tablename}}", null, null); 5578 $this->assertDebuggingNotCalled(); 5579 $DB->get_records_sql("SELECT * FROM {{$tablename}}", null, 1, null); 5580 $this->assertDebuggingNotCalled(); 5581 5582 // Verify that empty arrays DO create debugging mesages. 5583 $DB->get_records_sql("SELECT * FROM {{$tablename}}", null, array()); 5584 $this->assertDebuggingCalled("Non-numeric limitfrom parameter detected: array (\n), did you pass the correct arguments?"); 5585 $DB->get_records_sql("SELECT * FROM {{$tablename}}", null, 1, array()); 5586 $this->assertDebuggingCalled("Non-numeric limitnum parameter detected: array (\n), did you pass the correct arguments?"); 5587 5588 // Verify Negative number handling: 5589 // -1 is explicitly treated as 0 for historical reasons. 5590 $DB->get_records_sql("SELECT * FROM {{$tablename}}", null, -1); 5591 $this->assertDebuggingNotCalled(); 5592 $DB->get_records_sql("SELECT * FROM {{$tablename}}", null, 1, -1); 5593 $this->assertDebuggingNotCalled(); 5594 // Any other negative values should throw debugging messages. 5595 $DB->get_records_sql("SELECT * FROM {{$tablename}}", null, -2); 5596 $this->assertDebuggingCalled("Negative limitfrom parameter detected: -2, did you pass the correct arguments?"); 5597 $DB->get_records_sql("SELECT * FROM {{$tablename}}", null, 1, -2); 5598 $this->assertDebuggingCalled("Negative limitnum parameter detected: -2, did you pass the correct arguments?"); 5599 } 5600 5601 public function test_queries_counter() { 5602 5603 $DB = $this->tdb; 5604 $dbman = $this->tdb->get_manager(); 5605 5606 // Test database. 5607 $table = $this->get_test_table(); 5608 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 5609 $table->add_field('fieldvalue', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 5610 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 5611 5612 $dbman->create_table($table); 5613 $tablename = $table->getName(); 5614 5615 // Initial counters values. 5616 $initreads = $DB->perf_get_reads(); 5617 $initwrites = $DB->perf_get_writes(); 5618 $previousqueriestime = $DB->perf_get_queries_time(); 5619 5620 // Selects counts as reads. 5621 5622 // The get_records_sql() method generates only 1 db query. 5623 $whatever = $DB->get_records_sql("SELECT * FROM {{$tablename}}"); 5624 $this->assertEquals($initreads + 1, $DB->perf_get_reads()); 5625 5626 // The get_records() method generates 2 queries the first time is called 5627 // as it is fetching the table structure. 5628 $whatever = $DB->get_records($tablename, array('id' => '1')); 5629 $this->assertEquals($initreads + 3, $DB->perf_get_reads()); 5630 $this->assertEquals($initwrites, $DB->perf_get_writes()); 5631 5632 // The elapsed time is counted. 5633 $lastqueriestime = $DB->perf_get_queries_time(); 5634 $this->assertGreaterThanOrEqual($previousqueriestime, $lastqueriestime); 5635 $previousqueriestime = $lastqueriestime; 5636 5637 // Only 1 now, it already fetched the table columns. 5638 $whatever = $DB->get_records($tablename); 5639 $this->assertEquals($initreads + 4, $DB->perf_get_reads()); 5640 5641 // And only 1 more from now. 5642 $whatever = $DB->get_records($tablename); 5643 $this->assertEquals($initreads + 5, $DB->perf_get_reads()); 5644 5645 // Inserts counts as writes. 5646 5647 $rec1 = new stdClass(); 5648 $rec1->fieldvalue = 11; 5649 $rec1->id = $DB->insert_record($tablename, $rec1); 5650 $this->assertEquals($initwrites + 1, $DB->perf_get_writes()); 5651 $this->assertEquals($initreads + 5, $DB->perf_get_reads()); 5652 5653 // The elapsed time is counted. 5654 $lastqueriestime = $DB->perf_get_queries_time(); 5655 $this->assertGreaterThanOrEqual($previousqueriestime, $lastqueriestime); 5656 $previousqueriestime = $lastqueriestime; 5657 5658 $rec2 = new stdClass(); 5659 $rec2->fieldvalue = 22; 5660 $rec2->id = $DB->insert_record($tablename, $rec2); 5661 $this->assertEquals($initwrites + 2, $DB->perf_get_writes()); 5662 5663 // Updates counts as writes. 5664 5665 $rec1->fieldvalue = 111; 5666 $DB->update_record($tablename, $rec1); 5667 $this->assertEquals($initwrites + 3, $DB->perf_get_writes()); 5668 $this->assertEquals($initreads + 5, $DB->perf_get_reads()); 5669 5670 // The elapsed time is counted. 5671 $lastqueriestime = $DB->perf_get_queries_time(); 5672 $this->assertGreaterThanOrEqual($previousqueriestime, $lastqueriestime); 5673 $previousqueriestime = $lastqueriestime; 5674 5675 // Sum of them. 5676 $totaldbqueries = $DB->perf_get_reads() + $DB->perf_get_writes(); 5677 $this->assertEquals($totaldbqueries, $DB->perf_get_queries()); 5678 } 5679 5680 public function test_sql_intersect() { 5681 $DB = $this->tdb; 5682 $dbman = $this->tdb->get_manager(); 5683 5684 $tables = array(); 5685 for ($i = 0; $i < 3; $i++) { 5686 $table = $this->get_test_table('i'.$i); 5687 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 5688 $table->add_field('ival', XMLDB_TYPE_INTEGER, '10', null, null, null, null); 5689 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0'); 5690 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 5691 $dbman->create_table($table); 5692 $tables[$i] = $table; 5693 } 5694 $DB->insert_record($tables[0]->getName(), array('ival' => 1, 'name' => 'One'), false); 5695 $DB->insert_record($tables[0]->getName(), array('ival' => 2, 'name' => 'Two'), false); 5696 $DB->insert_record($tables[0]->getName(), array('ival' => 3, 'name' => 'Three'), false); 5697 $DB->insert_record($tables[0]->getName(), array('ival' => 4, 'name' => 'Four'), false); 5698 5699 $DB->insert_record($tables[1]->getName(), array('ival' => 1, 'name' => 'One'), false); 5700 $DB->insert_record($tables[1]->getName(), array('ival' => 2, 'name' => 'Two'), false); 5701 $DB->insert_record($tables[1]->getName(), array('ival' => 3, 'name' => 'Three'), false); 5702 5703 $DB->insert_record($tables[2]->getName(), array('ival' => 1, 'name' => 'One'), false); 5704 $DB->insert_record($tables[2]->getName(), array('ival' => 2, 'name' => 'Two'), false); 5705 $DB->insert_record($tables[2]->getName(), array('ival' => 5, 'name' => 'Five'), false); 5706 5707 // Intersection on the int column. 5708 $params = array('excludename' => 'Two'); 5709 $sql1 = 'SELECT ival FROM {'.$tables[0]->getName().'}'; 5710 $sql2 = 'SELECT ival FROM {'.$tables[1]->getName().'} WHERE name <> :excludename'; 5711 $sql3 = 'SELECT ival FROM {'.$tables[2]->getName().'}'; 5712 5713 $sql = $DB->sql_intersect(array($sql1), 'ival') . ' ORDER BY ival'; 5714 $this->assertEquals(array(1, 2, 3, 4), $DB->get_fieldset_sql($sql, $params)); 5715 5716 $sql = $DB->sql_intersect(array($sql1, $sql2), 'ival') . ' ORDER BY ival'; 5717 $this->assertEquals(array(1, 3), $DB->get_fieldset_sql($sql, $params)); 5718 5719 $sql = $DB->sql_intersect(array($sql1, $sql2, $sql3), 'ival') . ' ORDER BY ival'; 5720 $this->assertEquals(array(1), 5721 $DB->get_fieldset_sql($sql, $params)); 5722 5723 // Intersection on the char column. 5724 $params = array('excludeival' => 2); 5725 $sql1 = 'SELECT name FROM {'.$tables[0]->getName().'}'; 5726 $sql2 = 'SELECT name FROM {'.$tables[1]->getName().'} WHERE ival <> :excludeival'; 5727 $sql3 = 'SELECT name FROM {'.$tables[2]->getName().'}'; 5728 5729 $sql = $DB->sql_intersect(array($sql1), 'name') . ' ORDER BY name'; 5730 $this->assertEquals(array('Four', 'One', 'Three', 'Two'), $DB->get_fieldset_sql($sql, $params)); 5731 5732 $sql = $DB->sql_intersect(array($sql1, $sql2), 'name') . ' ORDER BY name'; 5733 $this->assertEquals(array('One', 'Three'), $DB->get_fieldset_sql($sql, $params)); 5734 5735 $sql = $DB->sql_intersect(array($sql1, $sql2, $sql3), 'name') . ' ORDER BY name'; 5736 $this->assertEquals(array('One'), $DB->get_fieldset_sql($sql, $params)); 5737 5738 // Intersection on the several columns. 5739 $params = array('excludename' => 'Two'); 5740 $sql1 = 'SELECT ival, name FROM {'.$tables[0]->getName().'}'; 5741 $sql2 = 'SELECT ival, name FROM {'.$tables[1]->getName().'} WHERE name <> :excludename'; 5742 $sql3 = 'SELECT ival, name FROM {'.$tables[2]->getName().'}'; 5743 5744 $sql = $DB->sql_intersect(array($sql1), 'ival, name') . ' ORDER BY ival'; 5745 $this->assertEquals(array(1 => 'One', 2 => 'Two', 3 => 'Three', 4 => 'Four'), 5746 $DB->get_records_sql_menu($sql, $params)); 5747 5748 $sql = $DB->sql_intersect(array($sql1, $sql2), 'ival, name') . ' ORDER BY ival'; 5749 $this->assertEquals(array(1 => 'One', 3 => 'Three'), 5750 $DB->get_records_sql_menu($sql, $params)); 5751 5752 $sql = $DB->sql_intersect(array($sql1, $sql2, $sql3), 'ival, name') . ' ORDER BY ival'; 5753 $this->assertEquals(array(1 => 'One'), 5754 $DB->get_records_sql_menu($sql, $params)); 5755 5756 // Drop temporary tables. 5757 foreach ($tables as $table) { 5758 $dbman->drop_table($table); 5759 } 5760 } 5761 5762 /** 5763 * Test that the database has full utf8 support (4 bytes). 5764 */ 5765 public function test_four_byte_character_insertion() { 5766 $DB = $this->tdb; 5767 5768 if ($DB->get_dbfamily() === 'mysql' && strpos($DB->get_dbcollation(), 'utf8_') === 0) { 5769 $this->markTestSkipped($DB->get_name() . 5770 ' does not support 4 byte characters with only a utf8 collation. 5771 Please change to utf8mb4 for full utf8 support.'); 5772 } 5773 5774 $dbman = $this->tdb->get_manager(); 5775 5776 $table = $this->get_test_table(); 5777 $tablename = $table->getName(); 5778 5779 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 5780 $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null); 5781 $table->add_field('content', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL); 5782 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 5783 $dbman->create_table($table); 5784 5785 $data = array( 5786 'name' => 'Name with a four byte character 𠮟る', 5787 'content' => 'Content with a four byte emoji 📝 memo.' 5788 ); 5789 5790 $insertid = $DB->insert_record($tablename, $data); 5791 $result = $DB->get_record($tablename, array('id' => $insertid)); 5792 $this->assertEquals($data['name'], $result->name); 5793 $this->assertEquals($data['content'], $result->content); 5794 5795 $dbman->drop_table($table); 5796 } 5797 } 5798 5799 /** 5800 * This class is not a proper subclass of moodle_database. It is 5801 * intended to be used only in unit tests, in order to gain access to the 5802 * protected methods of moodle_database, and unit test them. 5803 */ 5804 class moodle_database_for_testing extends moodle_database { 5805 protected $prefix = 'mdl_'; 5806 5807 public function public_fix_table_names($sql) { 5808 return $this->fix_table_names($sql); 5809 } 5810 5811 public function driver_installed() {} 5812 public function get_dbfamily() {} 5813 protected function get_dbtype() {} 5814 protected function get_dblibrary() {} 5815 public function get_name() {} 5816 public function get_configuration_help() {} 5817 public function connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, array $dboptions=null) {} 5818 public function get_server_info() {} 5819 protected function allowed_param_types() {} 5820 public function get_last_error() {} 5821 public function get_tables($usecache=true) {} 5822 public function get_indexes($table) {} 5823 protected function fetch_columns(string $table): array { 5824 return []; 5825 } 5826 protected function normalise_value($column, $value) {} 5827 public function set_debug($state) {} 5828 public function get_debug() {} 5829 public function change_database_structure($sql, $tablenames = null) {} 5830 public function execute($sql, array $params=null) {} 5831 public function get_recordset_sql($sql, array $params=null, $limitfrom=0, $limitnum=0) {} 5832 public function get_records_sql($sql, array $params=null, $limitfrom=0, $limitnum=0) {} 5833 public function get_fieldset_sql($sql, array $params=null) {} 5834 public function insert_record_raw($table, $params, $returnid=true, $bulk=false, $customsequence=false) {} 5835 public function insert_record($table, $dataobject, $returnid=true, $bulk=false) {} 5836 public function import_record($table, $dataobject) {} 5837 public function update_record_raw($table, $params, $bulk=false) {} 5838 public function update_record($table, $dataobject, $bulk=false) {} 5839 public function set_field_select($table, $newfield, $newvalue, $select, array $params=null) {} 5840 public function delete_records_select($table, $select, array $params=null) {} 5841 public function sql_concat() {} 5842 public function sql_concat_join($separator="' '", $elements=array()) {} 5843 public function sql_substr($expr, $start, $length=false) {} 5844 public function begin_transaction() {} 5845 public function commit_transaction() {} 5846 public function rollback_transaction() {} 5847 } 5848 5849 5850 /** 5851 * Dumb test class with toString() returning 1. 5852 */ 5853 class dml_test_object_one { 5854 public function __toString() { 5855 return 1; 5856 } 5857 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body