PageRenderTime 88ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/dml/tests/dml_test.php

https://bitbucket.org/synergylearning/campusconnect
PHP | 5130 lines | 4233 code | 608 blank | 289 comment | 39 complexity | 3adb6d4d0af7ac967f14386888f437a4 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, LGPL-2.1, Apache-2.0, BSD-3-Clause, AGPL-3.0
  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. * DML layer tests.
  18. *
  19. * @package core_dml
  20. * @category phpunit
  21. * @copyright 2008 Nicolas Connault
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23. */
  24. defined('MOODLE_INTERNAL') || die();
  25. class core_dml_testcase extends database_driver_testcase {
  26. protected function setUp() {
  27. parent::setUp();
  28. $dbman = $this->tdb->get_manager(); // Loads DDL libs.
  29. }
  30. /**
  31. * Get a xmldb_table object for testing, deleting any existing table
  32. * of the same name, for example if one was left over from a previous test
  33. * run that crashed.
  34. *
  35. * @param string $suffix table name suffix, use if you need more test tables
  36. * @return xmldb_table the table object.
  37. */
  38. private function get_test_table($suffix = '') {
  39. $tablename = "test_table";
  40. if ($suffix !== '') {
  41. $tablename .= $suffix;
  42. }
  43. $table = new xmldb_table($tablename);
  44. $table->setComment("This is a test'n drop table. You can drop it safely");
  45. return new xmldb_table($tablename);
  46. }
  47. public function test_diagnose() {
  48. $DB = $this->tdb;
  49. $result = $DB->diagnose();
  50. $this->assertNull($result, 'Database self diagnostics failed %s');
  51. }
  52. public function test_get_server_info() {
  53. $DB = $this->tdb;
  54. $result = $DB->get_server_info();
  55. $this->assertInternalType('array', $result);
  56. $this->assertArrayHasKey('description', $result);
  57. $this->assertArrayHasKey('version', $result);
  58. }
  59. public function test_get_in_or_equal() {
  60. $DB = $this->tdb;
  61. // SQL_PARAMS_QM - IN or =.
  62. // Correct usage of multiple values.
  63. $in_values = array('value1', 'value2', '3', 4, null, false, true);
  64. list($usql, $params) = $DB->get_in_or_equal($in_values);
  65. $this->assertSame('IN ('.implode(',', array_fill(0, count($in_values), '?')).')', $usql);
  66. $this->assertEquals(count($in_values), count($params));
  67. foreach ($params as $key => $value) {
  68. $this->assertSame($in_values[$key], $value);
  69. }
  70. // Correct usage of single value (in an array).
  71. $in_values = array('value1');
  72. list($usql, $params) = $DB->get_in_or_equal($in_values);
  73. $this->assertEquals("= ?", $usql);
  74. $this->assertCount(1, $params);
  75. $this->assertEquals($in_values[0], $params[0]);
  76. // Correct usage of single value.
  77. $in_value = 'value1';
  78. list($usql, $params) = $DB->get_in_or_equal($in_values);
  79. $this->assertEquals("= ?", $usql);
  80. $this->assertCount(1, $params);
  81. $this->assertEquals($in_value, $params[0]);
  82. // SQL_PARAMS_QM - NOT IN or <>.
  83. // Correct usage of multiple values.
  84. $in_values = array('value1', 'value2', 'value3', 'value4');
  85. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
  86. $this->assertEquals("NOT IN (?,?,?,?)", $usql);
  87. $this->assertCount(4, $params);
  88. foreach ($params as $key => $value) {
  89. $this->assertEquals($in_values[$key], $value);
  90. }
  91. // Correct usage of single value (in array().
  92. $in_values = array('value1');
  93. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
  94. $this->assertEquals("<> ?", $usql);
  95. $this->assertCount(1, $params);
  96. $this->assertEquals($in_values[0], $params[0]);
  97. // Correct usage of single value.
  98. $in_value = 'value1';
  99. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
  100. $this->assertEquals("<> ?", $usql);
  101. $this->assertCount(1, $params);
  102. $this->assertEquals($in_value, $params[0]);
  103. // SQL_PARAMS_NAMED - IN or =.
  104. // Correct usage of multiple values.
  105. $in_values = array('value1', 'value2', 'value3', 'value4');
  106. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true);
  107. $this->assertCount(4, $params);
  108. reset($in_values);
  109. $ps = array();
  110. foreach ($params as $key => $value) {
  111. $this->assertEquals(current($in_values), $value);
  112. next($in_values);
  113. $ps[] = ':'.$key;
  114. }
  115. $this->assertEquals("IN (".implode(',', $ps).")", $usql);
  116. // Correct usage of single values (in array).
  117. $in_values = array('value1');
  118. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true);
  119. $this->assertCount(1, $params);
  120. $value = reset($params);
  121. $key = key($params);
  122. $this->assertEquals("= :$key", $usql);
  123. $this->assertEquals($in_value, $value);
  124. // Correct usage of single value.
  125. $in_value = 'value1';
  126. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true);
  127. $this->assertCount(1, $params);
  128. $value = reset($params);
  129. $key = key($params);
  130. $this->assertEquals("= :$key", $usql);
  131. $this->assertEquals($in_value, $value);
  132. // SQL_PARAMS_NAMED - NOT IN or <>.
  133. // Correct usage of multiple values.
  134. $in_values = array('value1', 'value2', 'value3', 'value4');
  135. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
  136. $this->assertCount(4, $params);
  137. reset($in_values);
  138. $ps = array();
  139. foreach ($params as $key => $value) {
  140. $this->assertEquals(current($in_values), $value);
  141. next($in_values);
  142. $ps[] = ':'.$key;
  143. }
  144. $this->assertEquals("NOT IN (".implode(',', $ps).")", $usql);
  145. // Correct usage of single values (in array).
  146. $in_values = array('value1');
  147. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
  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. // Correct usage of single value.
  154. $in_value = 'value1';
  155. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
  156. $this->assertCount(1, $params);
  157. $value = reset($params);
  158. $key = key($params);
  159. $this->assertEquals("<> :$key", $usql);
  160. $this->assertEquals($in_value, $value);
  161. // Make sure the param names are unique.
  162. list($usql1, $params1) = $DB->get_in_or_equal(array(1, 2, 3), SQL_PARAMS_NAMED, 'param');
  163. list($usql2, $params2) = $DB->get_in_or_equal(array(1, 2, 3), SQL_PARAMS_NAMED, 'param');
  164. $params1 = array_keys($params1);
  165. $params2 = array_keys($params2);
  166. $common = array_intersect($params1, $params2);
  167. $this->assertCount(0, $common);
  168. // Some incorrect tests.
  169. // Incorrect usage passing not-allowed params type.
  170. $in_values = array(1, 2, 3);
  171. try {
  172. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_DOLLAR, 'param', false);
  173. $this->fail('An Exception is missing, expected due to not supported SQL_PARAMS_DOLLAR');
  174. } catch (moodle_exception $e) {
  175. $this->assertInstanceOf('dml_exception', $e);
  176. $this->assertSame('typenotimplement', $e->errorcode);
  177. }
  178. // Incorrect usage passing empty array.
  179. $in_values = array();
  180. try {
  181. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
  182. $this->fail('An Exception is missing, expected due to empty array of items');
  183. } catch (moodle_exception $e) {
  184. $this->assertInstanceOf('coding_exception', $e);
  185. }
  186. // Test using $onemptyitems.
  187. // Correct usage passing empty array and $onemptyitems = null (equal = true, QM).
  188. $in_values = array();
  189. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, null);
  190. $this->assertSame(' IS NULL', $usql);
  191. $this->assertSame(array(), $params);
  192. // Correct usage passing empty array and $onemptyitems = null (equal = false, NAMED).
  193. $in_values = array();
  194. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, null);
  195. $this->assertSame(' IS NOT NULL', $usql);
  196. $this->assertSame(array(), $params);
  197. // Correct usage passing empty array and $onemptyitems = true (equal = true, QM).
  198. $in_values = array();
  199. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, true);
  200. $this->assertSame('= ?', $usql);
  201. $this->assertSame(array(true), $params);
  202. // Correct usage passing empty array and $onemptyitems = true (equal = false, NAMED).
  203. $in_values = array();
  204. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, true);
  205. $this->assertCount(1, $params);
  206. $value = reset($params);
  207. $key = key($params);
  208. $this->assertSame('<> :'.$key, $usql);
  209. $this->assertSame($value, true);
  210. // Correct usage passing empty array and $onemptyitems = -1 (equal = true, QM).
  211. $in_values = array();
  212. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, -1);
  213. $this->assertSame('= ?', $usql);
  214. $this->assertSame(array(-1), $params);
  215. // Correct usage passing empty array and $onemptyitems = -1 (equal = false, NAMED).
  216. $in_values = array();
  217. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, -1);
  218. $this->assertCount(1, $params);
  219. $value = reset($params);
  220. $key = key($params);
  221. $this->assertSame('<> :'.$key, $usql);
  222. $this->assertSame($value, -1);
  223. // Correct usage passing empty array and $onemptyitems = 'onevalue' (equal = true, QM).
  224. $in_values = array();
  225. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, 'onevalue');
  226. $this->assertSame('= ?', $usql);
  227. $this->assertSame(array('onevalue'), $params);
  228. // Correct usage passing empty array and $onemptyitems = 'onevalue' (equal = false, NAMED).
  229. $in_values = array();
  230. list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, 'onevalue');
  231. $this->assertCount(1, $params);
  232. $value = reset($params);
  233. $key = key($params);
  234. $this->assertSame('<> :'.$key, $usql);
  235. $this->assertSame($value, 'onevalue');
  236. }
  237. public function test_fix_table_names() {
  238. $DB = new moodle_database_for_testing();
  239. $prefix = $DB->get_prefix();
  240. // Simple placeholder.
  241. $placeholder = "{user_123}";
  242. $this->assertSame($prefix."user_123", $DB->public_fix_table_names($placeholder));
  243. // Wrong table name.
  244. $placeholder = "{user-a}";
  245. $this->assertSame($placeholder, $DB->public_fix_table_names($placeholder));
  246. // Wrong table name.
  247. $placeholder = "{123user}";
  248. $this->assertSame($placeholder, $DB->public_fix_table_names($placeholder));
  249. // Full SQL.
  250. $sql = "SELECT * FROM {user}, {funny_table_name}, {mdl_stupid_table} WHERE {user}.id = {funny_table_name}.userid";
  251. $expected = "SELECT * FROM {$prefix}user, {$prefix}funny_table_name, {$prefix}mdl_stupid_table WHERE {$prefix}user.id = {$prefix}funny_table_name.userid";
  252. $this->assertSame($expected, $DB->public_fix_table_names($sql));
  253. }
  254. public function test_fix_sql_params() {
  255. $DB = $this->tdb;
  256. $prefix = $DB->get_prefix();
  257. $table = $this->get_test_table();
  258. $tablename = $table->getName();
  259. // Correct table placeholder substitution.
  260. $sql = "SELECT * FROM {{$tablename}}";
  261. $sqlarray = $DB->fix_sql_params($sql);
  262. $this->assertEquals("SELECT * FROM {$prefix}".$tablename, $sqlarray[0]);
  263. // Conversions of all param types.
  264. $sql = array();
  265. $sql[SQL_PARAMS_NAMED] = "SELECT * FROM {$prefix}testtable WHERE name = :param1, course = :param2";
  266. $sql[SQL_PARAMS_QM] = "SELECT * FROM {$prefix}testtable WHERE name = ?, course = ?";
  267. $sql[SQL_PARAMS_DOLLAR] = "SELECT * FROM {$prefix}testtable WHERE name = \$1, course = \$2";
  268. $params = array();
  269. $params[SQL_PARAMS_NAMED] = array('param1'=>'first record', 'param2'=>1);
  270. $params[SQL_PARAMS_QM] = array('first record', 1);
  271. $params[SQL_PARAMS_DOLLAR] = array('first record', 1);
  272. list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_NAMED], $params[SQL_PARAMS_NAMED]);
  273. $this->assertSame($rsql, $sql[$rtype]);
  274. $this->assertSame($rparams, $params[$rtype]);
  275. list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_QM], $params[SQL_PARAMS_QM]);
  276. $this->assertSame($rsql, $sql[$rtype]);
  277. $this->assertSame($rparams, $params[$rtype]);
  278. list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_DOLLAR], $params[SQL_PARAMS_DOLLAR]);
  279. $this->assertSame($rsql, $sql[$rtype]);
  280. $this->assertSame($rparams, $params[$rtype]);
  281. // Malformed table placeholder.
  282. $sql = "SELECT * FROM [testtable]";
  283. $sqlarray = $DB->fix_sql_params($sql);
  284. $this->assertSame($sql, $sqlarray[0]);
  285. // Mixed param types (colon and dollar).
  286. $sql = "SELECT * FROM {{$tablename}} WHERE name = :param1, course = \$1";
  287. $params = array('param1' => 'record1', 'param2' => 3);
  288. try {
  289. $DB->fix_sql_params($sql, $params);
  290. $this->fail("Expecting an exception, none occurred");
  291. } catch (moodle_exception $e) {
  292. $this->assertInstanceOf('dml_exception', $e);
  293. }
  294. // Mixed param types (question and dollar).
  295. $sql = "SELECT * FROM {{$tablename}} WHERE name = ?, course = \$1";
  296. $params = array('param1' => 'record2', 'param2' => 5);
  297. try {
  298. $DB->fix_sql_params($sql, $params);
  299. $this->fail("Expecting an exception, none occurred");
  300. } catch (moodle_exception $e) {
  301. $this->assertInstanceOf('dml_exception', $e);
  302. }
  303. // Too few params in sql.
  304. $sql = "SELECT * FROM {{$tablename}} WHERE name = ?, course = ?, id = ?";
  305. $params = array('record2', 3);
  306. try {
  307. $DB->fix_sql_params($sql, $params);
  308. $this->fail("Expecting an exception, none occurred");
  309. } catch (moodle_exception $e) {
  310. $this->assertInstanceOf('dml_exception', $e);
  311. }
  312. // Too many params in array: no error, just use what is necessary.
  313. $params[] = 1;
  314. $params[] = time();
  315. $sqlarray = $DB->fix_sql_params($sql, $params);
  316. $this->assertInternalType('array', $sqlarray);
  317. $this->assertCount(3, $sqlarray[1]);
  318. // Named params missing from array.
  319. $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :course";
  320. $params = array('wrongname' => 'record1', 'course' => 1);
  321. try {
  322. $DB->fix_sql_params($sql, $params);
  323. $this->fail("Expecting an exception, none occurred");
  324. } catch (moodle_exception $e) {
  325. $this->assertInstanceOf('dml_exception', $e);
  326. }
  327. // Duplicate named param in query - this is a very important feature!!
  328. // it helps with debugging of sloppy code.
  329. $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :name";
  330. $params = array('name' => 'record2', 'course' => 3);
  331. try {
  332. $DB->fix_sql_params($sql, $params);
  333. $this->fail("Expecting an exception, none occurred");
  334. } catch (moodle_exception $e) {
  335. $this->assertInstanceOf('dml_exception', $e);
  336. }
  337. // Extra named param is ignored.
  338. $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :course";
  339. $params = array('name' => 'record1', 'course' => 1, 'extrastuff'=>'haha');
  340. $sqlarray = $DB->fix_sql_params($sql, $params);
  341. $this->assertInternalType('array', $sqlarray);
  342. $this->assertCount(2, $sqlarray[1]);
  343. // Params exceeding 30 chars length.
  344. $sql = "SELECT * FROM {{$tablename}} WHERE name = :long_placeholder_with_more_than_30";
  345. $params = array('long_placeholder_with_more_than_30' => 'record1');
  346. try {
  347. $DB->fix_sql_params($sql, $params);
  348. $this->fail("Expecting an exception, none occurred");
  349. } catch (moodle_exception $e) {
  350. $this->assertInstanceOf('coding_exception', $e);
  351. }
  352. // Booleans in NAMED params are casting to 1/0 int.
  353. $sql = "SELECT * FROM {{$tablename}} WHERE course = ? OR course = ?";
  354. $params = array(true, false);
  355. list($sql, $params) = $DB->fix_sql_params($sql, $params);
  356. $this->assertTrue(reset($params) === 1);
  357. $this->assertTrue(next($params) === 0);
  358. // Booleans in QM params are casting to 1/0 int.
  359. $sql = "SELECT * FROM {{$tablename}} WHERE course = :course1 OR course = :course2";
  360. $params = array('course1' => true, 'course2' => false);
  361. list($sql, $params) = $DB->fix_sql_params($sql, $params);
  362. $this->assertTrue(reset($params) === 1);
  363. $this->assertTrue(next($params) === 0);
  364. // Booleans in DOLLAR params are casting to 1/0 int.
  365. $sql = "SELECT * FROM {{$tablename}} WHERE course = \$1 OR course = \$2";
  366. $params = array(true, false);
  367. list($sql, $params) = $DB->fix_sql_params($sql, $params);
  368. $this->assertTrue(reset($params) === 1);
  369. $this->assertTrue(next($params) === 0);
  370. // No data types are touched except bool.
  371. $sql = "SELECT * FROM {{$tablename}} WHERE name IN (?,?,?,?,?,?)";
  372. $inparams = array('abc', 'ABC', null, '1', 1, 1.4);
  373. list($sql, $params) = $DB->fix_sql_params($sql, $inparams);
  374. $this->assertSame(array_values($params), array_values($inparams));
  375. }
  376. public function test_strtok() {
  377. // Strtok was previously used by bound emulation, make sure it is not used any more.
  378. $DB = $this->tdb;
  379. $dbman = $this->tdb->get_manager();
  380. $table = $this->get_test_table();
  381. $tablename = $table->getName();
  382. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  383. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  384. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala');
  385. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  386. $dbman->create_table($table);
  387. $str = 'a?b?c?d';
  388. $this->assertSame(strtok($str, '?'), 'a');
  389. $DB->get_records($tablename, array('id'=>1));
  390. $this->assertSame(strtok('?'), 'b');
  391. }
  392. public function test_tweak_param_names() {
  393. // Note the tweak_param_names() method is only available in the oracle driver,
  394. // hence we look for expected results indirectly, by testing various DML methods.
  395. // with some "extreme" conditions causing the tweak to happen.
  396. $DB = $this->tdb;
  397. $dbman = $this->tdb->get_manager();
  398. $table = $this->get_test_table();
  399. $tablename = $table->getName();
  400. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  401. // Add some columns with 28 chars in the name.
  402. $table->add_field('long_int_columnname_with_28c', XMLDB_TYPE_INTEGER, '10');
  403. $table->add_field('long_dec_columnname_with_28c', XMLDB_TYPE_NUMBER, '10,2');
  404. $table->add_field('long_str_columnname_with_28c', XMLDB_TYPE_CHAR, '100');
  405. // Add some columns with 30 chars in the name.
  406. $table->add_field('long_int_columnname_with_30cxx', XMLDB_TYPE_INTEGER, '10');
  407. $table->add_field('long_dec_columnname_with_30cxx', XMLDB_TYPE_NUMBER, '10,2');
  408. $table->add_field('long_str_columnname_with_30cxx', XMLDB_TYPE_CHAR, '100');
  409. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  410. $dbman->create_table($table);
  411. $this->assertTrue($dbman->table_exists($tablename));
  412. // Test insert record.
  413. $rec1 = new stdClass();
  414. $rec1->long_int_columnname_with_28c = 28;
  415. $rec1->long_dec_columnname_with_28c = 28.28;
  416. $rec1->long_str_columnname_with_28c = '28';
  417. $rec1->long_int_columnname_with_30cxx = 30;
  418. $rec1->long_dec_columnname_with_30cxx = 30.30;
  419. $rec1->long_str_columnname_with_30cxx = '30';
  420. // Insert_record().
  421. $rec1->id = $DB->insert_record($tablename, $rec1);
  422. $this->assertEquals($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
  423. // Update_record().
  424. $DB->update_record($tablename, $rec1);
  425. $this->assertEquals($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
  426. // Set_field().
  427. $rec1->long_int_columnname_with_28c = 280;
  428. $DB->set_field($tablename, 'long_int_columnname_with_28c', $rec1->long_int_columnname_with_28c,
  429. array('id' => $rec1->id, 'long_int_columnname_with_28c' => 28));
  430. $rec1->long_dec_columnname_with_28c = 280.28;
  431. $DB->set_field($tablename, 'long_dec_columnname_with_28c', $rec1->long_dec_columnname_with_28c,
  432. array('id' => $rec1->id, 'long_dec_columnname_with_28c' => 28.28));
  433. $rec1->long_str_columnname_with_28c = '280';
  434. $DB->set_field($tablename, 'long_str_columnname_with_28c', $rec1->long_str_columnname_with_28c,
  435. array('id' => $rec1->id, 'long_str_columnname_with_28c' => '28'));
  436. $rec1->long_int_columnname_with_30cxx = 300;
  437. $DB->set_field($tablename, 'long_int_columnname_with_30cxx', $rec1->long_int_columnname_with_30cxx,
  438. array('id' => $rec1->id, 'long_int_columnname_with_30cxx' => 30));
  439. $rec1->long_dec_columnname_with_30cxx = 300.30;
  440. $DB->set_field($tablename, 'long_dec_columnname_with_30cxx', $rec1->long_dec_columnname_with_30cxx,
  441. array('id' => $rec1->id, 'long_dec_columnname_with_30cxx' => 30.30));
  442. $rec1->long_str_columnname_with_30cxx = '300';
  443. $DB->set_field($tablename, 'long_str_columnname_with_30cxx', $rec1->long_str_columnname_with_30cxx,
  444. array('id' => $rec1->id, 'long_str_columnname_with_30cxx' => '30'));
  445. $this->assertEquals($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
  446. // Delete_records().
  447. $rec2 = $DB->get_record($tablename, array('id' => $rec1->id));
  448. $rec2->id = $DB->insert_record($tablename, $rec2);
  449. $this->assertEquals(2, $DB->count_records($tablename));
  450. $DB->delete_records($tablename, (array) $rec2);
  451. $this->assertEquals(1, $DB->count_records($tablename));
  452. // Get_recordset().
  453. $rs = $DB->get_recordset($tablename, (array) $rec1);
  454. $iterations = 0;
  455. foreach ($rs as $rec2) {
  456. $iterations++;
  457. }
  458. $rs->close();
  459. $this->assertEquals(1, $iterations);
  460. $this->assertEquals($rec1, $rec2);
  461. // Get_records().
  462. $recs = $DB->get_records($tablename, (array) $rec1);
  463. $this->assertCount(1, $recs);
  464. $this->assertEquals($rec1, reset($recs));
  465. // Get_fieldset_select().
  466. $select = 'id = :id AND
  467. long_int_columnname_with_28c = :long_int_columnname_with_28c AND
  468. long_dec_columnname_with_28c = :long_dec_columnname_with_28c AND
  469. long_str_columnname_with_28c = :long_str_columnname_with_28c AND
  470. long_int_columnname_with_30cxx = :long_int_columnname_with_30cxx AND
  471. long_dec_columnname_with_30cxx = :long_dec_columnname_with_30cxx AND
  472. long_str_columnname_with_30cxx = :long_str_columnname_with_30cxx';
  473. $fields = $DB->get_fieldset_select($tablename, 'long_int_columnname_with_28c', $select, (array)$rec1);
  474. $this->assertCount(1, $fields);
  475. $this->assertEquals($rec1->long_int_columnname_with_28c, reset($fields));
  476. $fields = $DB->get_fieldset_select($tablename, 'long_dec_columnname_with_28c', $select, (array)$rec1);
  477. $this->assertEquals($rec1->long_dec_columnname_with_28c, reset($fields));
  478. $fields = $DB->get_fieldset_select($tablename, 'long_str_columnname_with_28c', $select, (array)$rec1);
  479. $this->assertEquals($rec1->long_str_columnname_with_28c, reset($fields));
  480. $fields = $DB->get_fieldset_select($tablename, 'long_int_columnname_with_30cxx', $select, (array)$rec1);
  481. $this->assertEquals($rec1->long_int_columnname_with_30cxx, reset($fields));
  482. $fields = $DB->get_fieldset_select($tablename, 'long_dec_columnname_with_30cxx', $select, (array)$rec1);
  483. $this->assertEquals($rec1->long_dec_columnname_with_30cxx, reset($fields));
  484. $fields = $DB->get_fieldset_select($tablename, 'long_str_columnname_with_30cxx', $select, (array)$rec1);
  485. $this->assertEquals($rec1->long_str_columnname_with_30cxx, reset($fields));
  486. // Overlapping placeholders (progressive str_replace).
  487. $overlapselect = 'id = :p AND
  488. long_int_columnname_with_28c = :param1 AND
  489. long_dec_columnname_with_28c = :param2 AND
  490. long_str_columnname_with_28c = :param_with_29_characters_long AND
  491. long_int_columnname_with_30cxx = :param_with_30_characters_long_ AND
  492. long_dec_columnname_with_30cxx = :param_ AND
  493. long_str_columnname_with_30cxx = :param__';
  494. $overlapparams = array(
  495. 'p' => $rec1->id,
  496. 'param1' => $rec1->long_int_columnname_with_28c,
  497. 'param2' => $rec1->long_dec_columnname_with_28c,
  498. 'param_with_29_characters_long' => $rec1->long_str_columnname_with_28c,
  499. 'param_with_30_characters_long_' => $rec1->long_int_columnname_with_30cxx,
  500. 'param_' => $rec1->long_dec_columnname_with_30cxx,
  501. 'param__' => $rec1->long_str_columnname_with_30cxx);
  502. $recs = $DB->get_records_select($tablename, $overlapselect, $overlapparams);
  503. $this->assertCount(1, $recs);
  504. $this->assertEquals($rec1, reset($recs));
  505. // Execute().
  506. $DB->execute("DELETE FROM {{$tablename}} WHERE $select", (array)$rec1);
  507. $this->assertEquals(0, $DB->count_records($tablename));
  508. }
  509. public function test_get_tables() {
  510. $DB = $this->tdb;
  511. $dbman = $this->tdb->get_manager();
  512. // Need to test with multiple DBs.
  513. $table = $this->get_test_table();
  514. $tablename = $table->getName();
  515. $original_count = count($DB->get_tables());
  516. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  517. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  518. $dbman->create_table($table);
  519. $this->assertTrue(count($DB->get_tables()) == $original_count + 1);
  520. $dbman->drop_table($table);
  521. $this->assertTrue(count($DB->get_tables()) == $original_count);
  522. }
  523. public function test_get_indexes() {
  524. $DB = $this->tdb;
  525. $dbman = $this->tdb->get_manager();
  526. $table = $this->get_test_table();
  527. $tablename = $table->getName();
  528. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  529. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  530. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  531. $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
  532. $table->add_index('course-id', XMLDB_INDEX_UNIQUE, array('course', 'id'));
  533. $dbman->create_table($table);
  534. $indices = $DB->get_indexes($tablename);
  535. $this->assertInternalType('array', $indices);
  536. $this->assertCount(2, $indices);
  537. // We do not care about index names for now.
  538. $first = array_shift($indices);
  539. $second = array_shift($indices);
  540. if (count($first['columns']) == 2) {
  541. $composed = $first;
  542. $single = $second;
  543. } else {
  544. $composed = $second;
  545. $single = $first;
  546. }
  547. $this->assertFalse($single['unique']);
  548. $this->assertTrue($composed['unique']);
  549. $this->assertCount(1, $single['columns']);
  550. $this->assertCount(2, $composed['columns']);
  551. $this->assertSame('course', $single['columns'][0]);
  552. $this->assertSame('course', $composed['columns'][0]);
  553. $this->assertSame('id', $composed['columns'][1]);
  554. }
  555. public function test_get_columns() {
  556. $DB = $this->tdb;
  557. $dbman = $this->tdb->get_manager();
  558. $table = $this->get_test_table();
  559. $tablename = $table->getName();
  560. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  561. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  562. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala');
  563. $table->add_field('description', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
  564. $table->add_field('enumfield', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'test2');
  565. $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
  566. $table->add_field('onefloat', XMLDB_TYPE_FLOAT, '10,2', null, null, null, 300);
  567. $table->add_field('anotherfloat', XMLDB_TYPE_FLOAT, null, null, null, null, 400);
  568. $table->add_field('negativedfltint', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '-1');
  569. $table->add_field('negativedfltnumber', XMLDB_TYPE_NUMBER, '10', null, XMLDB_NOTNULL, null, '-2');
  570. $table->add_field('negativedfltfloat', XMLDB_TYPE_FLOAT, '10', null, XMLDB_NOTNULL, null, '-3');
  571. $table->add_field('someint1', XMLDB_TYPE_INTEGER, '1', null, null, null, '0');
  572. $table->add_field('someint2', XMLDB_TYPE_INTEGER, '2', null, null, null, '0');
  573. $table->add_field('someint3', XMLDB_TYPE_INTEGER, '3', null, null, null, '0');
  574. $table->add_field('someint4', XMLDB_TYPE_INTEGER, '4', null, null, null, '0');
  575. $table->add_field('someint5', XMLDB_TYPE_INTEGER, '5', null, null, null, '0');
  576. $table->add_field('someint6', XMLDB_TYPE_INTEGER, '6', null, null, null, '0');
  577. $table->add_field('someint7', XMLDB_TYPE_INTEGER, '7', null, null, null, '0');
  578. $table->add_field('someint8', XMLDB_TYPE_INTEGER, '8', null, null, null, '0');
  579. $table->add_field('someint9', XMLDB_TYPE_INTEGER, '9', null, null, null, '0');
  580. $table->add_field('someint10', XMLDB_TYPE_INTEGER, '10', null, null, null, '0');
  581. $table->add_field('someint18', XMLDB_TYPE_INTEGER, '18', null, null, null, '0');
  582. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  583. $dbman->create_table($table);
  584. $columns = $DB->get_columns($tablename);
  585. $this->assertInternalType('array', $columns);
  586. $fields = $table->getFields();
  587. $this->assertCount(count($columns), $fields);
  588. $field = $columns['id'];
  589. $this->assertSame('R', $field->meta_type);
  590. $this->assertTrue($field->auto_increment);
  591. $this->assertTrue($field->unique);
  592. $field = $columns['course'];
  593. $this->assertSame('I', $field->meta_type);
  594. $this->assertFalse($field->auto_increment);
  595. $this->assertTrue($field->has_default);
  596. $this->assertEquals(0, $field->default_value);
  597. $this->assertTrue($field->not_null);
  598. for ($i=1; $i<=10; $i++) {
  599. $field = $columns['someint'.$i];
  600. $this->assertSame('I', $field->meta_type);
  601. $this->assertGreaterThanOrEqual($i, $field->max_length);
  602. }
  603. $field = $columns['someint18'];
  604. $this->assertSame('I', $field->meta_type);
  605. $this->assertGreaterThanOrEqual(18, $field->max_length);
  606. $field = $columns['name'];
  607. $this->assertSame('C', $field->meta_type);
  608. $this->assertFalse($field->auto_increment);
  609. $this->assertEquals(255, $field->max_length);
  610. $this->assertTrue($field->has_default);
  611. $this->assertSame('lala', $field->default_value);
  612. $this->assertFalse($field->not_null);
  613. $field = $columns['description'];
  614. $this->assertSame('X', $field->meta_type);
  615. $this->assertFalse($field->auto_increment);
  616. $this->assertFalse($field->has_default);
  617. $this->assertNull($field->default_value);
  618. $this->assertFalse($field->not_null);
  619. $field = $columns['enumfield'];
  620. $this->assertSame('C', $field->meta_type);
  621. $this->assertFalse($field->auto_increment);
  622. $this->assertSame('test2', $field->default_value);
  623. $this->assertTrue($field->not_null);
  624. $field = $columns['onenum'];
  625. $this->assertSame('N', $field->meta_type);
  626. $this->assertFalse($field->auto_increment);
  627. $this->assertEquals(10, $field->max_length);
  628. $this->assertEquals(2, $field->scale);
  629. $this->assertTrue($field->has_default);
  630. $this->assertEquals(200.0, $field->default_value);
  631. $this->assertFalse($field->not_null);
  632. $field = $columns['onefloat'];
  633. $this->assertSame('N', $field->meta_type);
  634. $this->assertFalse($field->auto_increment);
  635. $this->assertTrue($field->has_default);
  636. $this->assertEquals(300.0, $field->default_value);
  637. $this->assertFalse($field->not_null);
  638. $field = $columns['anotherfloat'];
  639. $this->assertSame('N', $field->meta_type);
  640. $this->assertFalse($field->auto_increment);
  641. $this->assertTrue($field->has_default);
  642. $this->assertEquals(400.0, $field->default_value);
  643. $this->assertFalse($field->not_null);
  644. // Test negative defaults in numerical columns.
  645. $field = $columns['negativedfltint'];
  646. $this->assertTrue($field->has_default);
  647. $this->assertEquals(-1, $field->default_value);
  648. $field = $columns['negativedfltnumber'];
  649. $this->assertTrue($field->has_default);
  650. $this->assertEquals(-2, $field->default_value);
  651. $field = $columns['negativedfltfloat'];
  652. $this->assertTrue($field->has_default);
  653. $this->assertEquals(-3, $field->default_value);
  654. for ($i = 0; $i < count($columns); $i++) {
  655. if ($i == 0) {
  656. $next_column = reset($columns);
  657. $next_field = reset($fields);
  658. } else {
  659. $next_column = next($columns);
  660. $next_field = next($fields);
  661. }
  662. $this->assertEquals($next_column->name, $next_field->getName());
  663. }
  664. // Test get_columns for non-existing table returns empty array. MDL-30147.
  665. $columns = $DB->get_columns('xxxx');
  666. $this->assertEquals(array(), $columns);
  667. // Create something similar to "context_temp" with id column without sequence.
  668. $dbman->drop_table($table);
  669. $table = $this->get_test_table();
  670. $tablename = $table->getName();
  671. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
  672. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  673. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  674. $dbman->create_table($table);
  675. $columns = $DB->get_columns($tablename);
  676. $this->assertFalse($columns['id']->auto_increment);
  677. }
  678. public function test_get_manager() {
  679. $DB = $this->tdb;
  680. $dbman = $this->tdb->get_manager();
  681. $this->assertInstanceOf('database_manager', $dbman);
  682. }
  683. public function test_setup_is_unicodedb() {
  684. $DB = $this->tdb;
  685. $this->assertTrue($DB->setup_is_unicodedb());
  686. }
  687. public function test_set_debug() { // Tests get_debug() too.
  688. $DB = $this->tdb;
  689. $dbman = $this->tdb->get_manager();
  690. $table = $this->get_test_table();
  691. $tablename = $table->getName();
  692. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  693. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  694. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  695. $dbman->create_table($table);
  696. $sql = "SELECT * FROM {{$tablename}}";
  697. $prevdebug = $DB->get_debug();
  698. ob_start();
  699. $DB->set_debug(true);
  700. $this->assertTrue($DB->get_debug());
  701. $DB->execute($sql);
  702. $DB->set_debug(false);
  703. $this->assertFalse($DB->get_debug());
  704. $debuginfo = ob_get_contents();
  705. ob_end_clean();
  706. $this->assertFalse($debuginfo === '');
  707. ob_start();
  708. $DB->execute($sql);
  709. $debuginfo = ob_get_contents();
  710. ob_end_clean();
  711. $this->assertTrue($debuginfo === '');
  712. $DB->set_debug($prevdebug);
  713. }
  714. public function test_execute() {
  715. $DB = $this->tdb;
  716. $dbman = $this->tdb->get_manager();
  717. $table1 = $this->get_test_table('1');
  718. $tablename1 = $table1->getName();
  719. $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  720. $table1->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  721. $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
  722. $table1->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
  723. $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  724. $dbman->create_table($table1);
  725. $table2 = $this->get_test_table('2');
  726. $tablename2 = $table2->getName();
  727. $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  728. $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  729. $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  730. $dbman->create_table($table2);
  731. $DB->insert_record($tablename1, array('course' => 3, 'name' => 'aaa'));
  732. $DB->insert_record($tablename1, array('course' => 1, 'name' => 'bbb'));
  733. $DB->insert_record($tablename1, array('course' => 7, 'name' => 'ccc'));
  734. $DB->insert_record($tablename1, array('course' => 3, 'name' => 'ddd'));
  735. // Select results are ignored.
  736. $sql = "SELECT * FROM {{$tablename1}} WHERE course = :course";
  737. $this->assertTrue($DB->execute($sql, array('course'=>3)));
  738. // Throw exception on error.
  739. $sql = "XXUPDATE SET XSSD";
  740. try {
  741. $DB->execute($sql);
  742. $this->fail("Expecting an exception, none occurred");
  743. } catch (moodle_exception $e) {
  744. $this->assertInstanceOf('dml_exception', $e);
  745. }
  746. // Update records.
  747. $sql = "UPDATE {{$tablename1}}
  748. SET course = 6
  749. WHERE course = ?";
  750. $this->assertTrue($DB->execute($sql, array('3')));
  751. $this->assertEquals(2, $DB->count_records($tablename1, array('course' => 6)));
  752. // Update records with subquery condition.
  753. // Confirm that the option not using table aliases is cross-db.
  754. $sql = "UPDATE {{$tablename1}}
  755. SET course = 0
  756. WHERE NOT EXISTS (
  757. SELECT course
  758. FROM {{$tablename2}} tbl2
  759. WHERE tbl2.course = {{$tablename1}}.course
  760. AND 1 = 0)"; // Really we don't update anything, but verify the syntax is allowed.
  761. $this->assertTrue($DB->execute($sql));
  762. // Insert from one into second table.
  763. $sql = "INSERT INTO {{$tablename2}} (course)
  764. SELECT course
  765. FROM {{$tablename1}}";
  766. $this->assertTrue($DB->execute($sql));
  767. $this->assertEquals(4, $DB->count_records($tablename2));
  768. }
  769. public function test_get_recordset() {
  770. $DB = $this->tdb;
  771. $dbman = $DB->get_manager();
  772. $table = $this->get_test_table();
  773. $tablename = $table->getName();
  774. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  775. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  776. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
  777. $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
  778. $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
  779. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  780. $dbman->create_table($table);
  781. $data = array(array('course' => 3, 'name' => 'record1', 'onetext'=>'abc'),
  782. array('course' => 3, 'name' => 'record2', 'onetext'=>'abcd'),
  783. array('course' => 5, 'name' => 'record3', 'onetext'=>'abcde'));
  784. foreach ($data as $key => $record) {
  785. $data[$key]['id'] = $DB->insert_record($tablename, $record);
  786. }
  787. // Standard recordset iteration.
  788. $rs = $DB->get_recordset($tablename);
  789. $this->assertInstanceOf('moodle_recordset', $rs);
  790. reset($data);
  791. foreach ($rs as $record) {
  792. $data_record = current($data);
  793. foreach ($record as $k => $v) {
  794. $this->assertEquals($data_record[$k], $v);
  795. }
  796. next($data);
  797. }
  798. $rs->close();
  799. // Iterator style usage.
  800. $rs = $DB->get_recordset($tablename);
  801. $this->assertInstanceOf('moodle_recordset', $rs);
  802. reset($data);
  803. while ($rs->valid()) {
  804. $record = $rs->current();
  805. $data_record = current($data);
  806. foreach ($record as $k => $v) {
  807. $this->assertEquals($data_record[$k], $v);
  808. }
  809. next($data);
  810. $rs->next();
  811. }
  812. $rs->close();
  813. // Make sure rewind is ignored.
  814. $rs = $DB->get_recordset($tablename);
  815. $this->assertInstanceOf('moodle_recordset', $rs);
  816. reset($data);
  817. $i = 0;
  818. foreach ($rs as $record) {
  819. $i++;
  820. $rs->rewind();
  821. if ($i > 10) {
  822. $this->fail('revind not ignored in recordsets');
  823. break;
  824. }
  825. $data_record = current($data);
  826. foreach ($record as $k => $v) {
  827. $this->assertEquals($data_record[$k], $v);
  828. }
  829. next($data);
  830. }
  831. $rs->close();
  832. // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
  833. $conditions = array('onetext' => '1');
  834. try {
  835. $rs = $DB->get_recordset($tablename, $conditions);
  836. $this->fail('An Exception is missing, expected due to equating of text fields');
  837. } catch (moodle_exception $e) {
  838. $this->assertInstanceOf('dml_exception', $e);
  839. $this->assertSame('textconditionsnotallowed', $e->errorcode);
  840. }
  841. // Test nested iteration.
  842. $rs1 = $DB->get_recordset($tablename);
  843. $i = 0;
  844. foreach ($rs1 as $record1) {
  845. $rs2 = $DB->get_recordset($tablename);
  846. $i++;
  847. $j = 0;
  848. foreach ($rs2 as $record2) {
  849. $j++;
  850. }
  851. $rs2->close();
  852. $this->assertCount($j, $data);
  853. }
  854. $rs1->close();
  855. $this->assertCount($i, $data);
  856. // Notes:
  857. // * limits are tested in test_get_recordset_sql()
  858. // * where_clause() is used internally and is tested in test_get_records()
  859. }
  860. public function test_get_recordset_static() {
  861. $DB = $this->tdb;
  862. $dbman = $DB->get_manager();
  863. $table = $this->get_test_table();
  864. $tablename = $table->getName();
  865. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  866. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  867. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  868. $dbman->create_table($table);
  869. $DB->insert_record($tablename, array('course' => 1));
  870. $DB->insert_record($tablename, array('course' => 2));
  871. $DB->insert_record($tablename, array('course' => 3));
  872. $DB->insert_record($tablename, array('course' => 4));
  873. $rs = $DB->get_recordset($tablename, array(), 'id');
  874. $DB->set_field($tablename, 'course', 666, array('course'=>1));
  875. $DB->delete_records($tablename, array('course'=>2));
  876. $i = 0;
  877. foreach ($rs as $record) {
  878. $i++;
  879. $this->assertEquals($i, $record->course);
  880. }
  881. $rs->close();
  882. $this->assertEquals(4, $i);
  883. // Now repeat with limits because it may use different code.
  884. $DB->delete_records($tablename, array());
  885. $DB->insert_record($tablename, array('course' => 1));
  886. $DB->insert_record($tablename, array('course' => 2));
  887. $DB->insert_record($tablename, array('course' => 3));
  888. $DB->insert_record($tablename, array('course' => 4));
  889. $rs = $DB->get_recordset($tablename, array(), 'id', '*', 0, 3);
  890. $DB->set_field($tablename, 'course', 666, array('course'=>1));
  891. $DB->delete_records($tablename, array('course'=>2));
  892. $i = 0;
  893. foreach ($rs as $record) {
  894. $i++;
  895. $this->assertEquals($i, $record->course);
  896. }
  897. $rs->close();
  898. $this->assertEquals(3, $i);
  899. }
  900. public function test_get_recordset_iterator_keys() {
  901. $DB = $this->tdb;
  902. $dbman = $DB->get_manager();
  903. $table = $this->get_test_table();
  904. $tablename = $table->getName();
  905. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  906. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  907. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
  908. $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
  909. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  910. $dbman->create_table($table);
  911. $data = array(array('course' => 3, 'name' => 'record1'),
  912. array('course' => 3, 'name' => 'record2'),
  913. array('course' => 5, 'name' => 'record3'));
  914. foreach ($data as $key => $record) {
  915. $data[$key]['id'] = $DB->insert_record($tablename, $record);
  916. }
  917. // Test repeated numeric keys are returned ok.
  918. $rs = $DB->get_recordset($tablename, null, null, 'course, name, id');
  919. reset($data);
  920. $count = 0;
  921. foreach ($rs as $key => $record) {
  922. $data_record = current($data);
  923. $this->assertEquals($data_record['course'], $key);
  924. next($data);
  925. $count++;
  926. }
  927. $rs->close();
  928. $this->assertEquals(3, $count);
  929. // Test string keys are returned ok.
  930. $rs = $DB->get_recordset($tablename, null, null, 'name, course, id');
  931. reset($data);
  932. $count = 0;
  933. foreach ($rs as $key => $record) {
  934. $data_record = current($data);
  935. $this->assertEquals($data_record['name'], $key);
  936. next($data);
  937. $count++;
  938. }
  939. $rs->close();
  940. $this->assertEquals(3, $count);
  941. // Test numeric not starting in 1 keys are returned ok.
  942. $rs = $DB->get_recordset($tablename, null, 'id DESC', 'id, course, name');
  943. $data = array_reverse($data);
  944. reset($data);
  945. $count = 0;
  946. foreach ($rs as $key => $record) {
  947. $data_record = current($data);
  948. $this->assertEquals($data_record['id'], $key);
  949. next($data);
  950. $count++;
  951. }
  952. $rs->close();
  953. $this->assertEquals(3, $count);
  954. }
  955. public function test_get_recordset_list() {
  956. $DB = $this->tdb;
  957. $dbman = $DB->get_manager();
  958. $table = $this->get_test_table();
  959. $tablename = $table->getName();
  960. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  961. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, null, null, '0');
  962. $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
  963. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  964. $dbman->create_table($table);
  965. $DB->insert_record($tablename, array('course' => 3));
  966. $DB->insert_record($tablename, array('course' => 3));
  967. $DB->insert_record($tablename, array('course' => 5));
  968. $DB->insert_record($tablename, array('course' => 2));
  969. $DB->insert_record($tablename, array('course' => null));
  970. $DB->insert_record($tablename, array('course' => 1));
  971. $DB->insert_record($tablename, array('course' => 0));
  972. $rs = $DB->get_recordset_list($tablename, 'course', array(3, 2));
  973. $counter = 0;
  974. foreach ($rs as $record) {
  975. $counter++;
  976. }
  977. $this->assertEquals(3, $counter);
  978. $rs->close();
  979. $rs = $DB->get_recordset_list($tablename, 'course', array(3));
  980. $counter = 0;
  981. foreach ($rs as $record) {
  982. $counter++;
  983. }
  984. $this->assertEquals(2, $counter);
  985. $rs->close();
  986. $rs = $DB->get_recordset_list($tablename, 'course', array(null));
  987. $counter = 0;
  988. foreach ($rs as $record) {
  989. $counter++;
  990. }
  991. $this->assertEquals(1, $counter);
  992. $rs->close();
  993. $rs = $DB->get_recordset_list($tablename, 'course', array(6, null));
  994. $counter = 0;
  995. foreach ($rs as $record) {
  996. $counter++;
  997. }
  998. $this->assertEquals(1, $counter);
  999. $rs->close();
  1000. $rs = $DB->get_recordset_list($tablename, 'course', array(null, 5, 5, 5));
  1001. $counter = 0;
  1002. foreach ($rs as $record) {
  1003. $counter++;
  1004. }
  1005. $this->assertEquals(2, $counter);
  1006. $rs->close();
  1007. $rs = $DB->get_recordset_list($tablename, 'course', array(true));
  1008. $counter = 0;
  1009. foreach ($rs as $record) {
  1010. $counter++;
  1011. }
  1012. $this->assertEquals(1, $counter);
  1013. $rs->close();
  1014. $rs = $DB->get_recordset_list($tablename, 'course', array(false));
  1015. $counter = 0;
  1016. foreach ($rs as $record) {
  1017. $counter++;
  1018. }
  1019. $this->assertEquals(1, $counter);
  1020. $rs->close();
  1021. $rs = $DB->get_recordset_list($tablename, 'course', array()); // Must return 0 rows without conditions. MDL-17645.
  1022. $counter = 0;
  1023. foreach ($rs as $record) {
  1024. $counter++;
  1025. }
  1026. $rs->close();
  1027. $this->assertEquals(0, $counter);
  1028. // Notes:
  1029. // * limits are tested in test_get_recordset_sql()
  1030. // * where_clause() is used internally and is tested in test_get_records()
  1031. }
  1032. public function test_get_recordset_select() {
  1033. $DB = $this->tdb;
  1034. $dbman = $DB->get_manager();
  1035. $table = $this->get_test_table();
  1036. $tablename = $table->getName();
  1037. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1038. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1039. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1040. $dbman->create_table($table);
  1041. $DB->insert_record($tablename, array('course' => 3));
  1042. $DB->insert_record($tablename, array('course' => 3));
  1043. $DB->insert_record($tablename, array('course' => 5));
  1044. $DB->insert_record($tablename, array('course' => 2));
  1045. $rs = $DB->get_recordset_select($tablename, '');
  1046. $counter = 0;
  1047. foreach ($rs as $record) {
  1048. $counter++;
  1049. }
  1050. $rs->close();
  1051. $this->assertEquals(4, $counter);
  1052. $this->assertNotEmpty($rs = $DB->get_recordset_select($tablename, 'course = 3'));
  1053. $counter = 0;
  1054. foreach ($rs as $record) {
  1055. $counter++;
  1056. }
  1057. $rs->close();
  1058. $this->assertEquals(2, $counter);
  1059. // Notes:
  1060. // * limits are tested in test_get_recordset_sql()
  1061. }
  1062. public function test_get_recordset_sql() {
  1063. $DB = $this->tdb;
  1064. $dbman = $DB->get_manager();
  1065. $table = $this->get_test_table();
  1066. $tablename = $table->getName();
  1067. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1068. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1069. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1070. $dbman->create_table($table);
  1071. $inskey1 = $DB->insert_record($tablename, array('course' => 3));
  1072. $inskey2 = $DB->insert_record($tablename, array('course' => 5));
  1073. $inskey3 = $DB->insert_record($tablename, array('course' => 4));
  1074. $inskey4 = $DB->insert_record($tablename, array('course' => 3));
  1075. $inskey5 = $DB->insert_record($tablename, array('course' => 2));
  1076. $inskey6 = $DB->insert_record($tablename, array('course' => 1));
  1077. $inskey7 = $DB->insert_record($tablename, array('course' => 0));
  1078. $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
  1079. $counter = 0;
  1080. foreach ($rs as $record) {
  1081. $counter++;
  1082. }
  1083. $rs->close();
  1084. $this->assertEquals(2, $counter);
  1085. // Limits - only need to test this case, the rest have been tested by test_get_records_sql()
  1086. // only limitfrom = skips that number of records.
  1087. $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
  1088. $records = array();
  1089. foreach ($rs as $key => $record) {
  1090. $records[$key] = $record;
  1091. }
  1092. $rs->close();
  1093. $this->assertCount(5, $records);
  1094. $this->assertEquals($inskey3, reset($records)->id);
  1095. $this->assertEquals($inskey7, end($records)->id);
  1096. // Note: fetching nulls, empties, LOBs already tested by test_insert_record() no needed here.
  1097. }
  1098. public function test_export_table_recordset() {
  1099. $DB = $this->tdb;
  1100. $dbman = $DB->get_manager();
  1101. $table = $this->get_test_table();
  1102. $tablename = $table->getName();
  1103. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1104. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1105. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1106. $dbman->create_table($table);
  1107. $ids = array();
  1108. $ids[] = $DB->insert_record($tablename, array('course' => 3));
  1109. $ids[] = $DB->insert_record($tablename, array('course' => 5));
  1110. $ids[] = $DB->insert_record($tablename, array('course' => 4));
  1111. $ids[] = $DB->insert_record($tablename, array('course' => 3));
  1112. $ids[] = $DB->insert_record($tablename, array('course' => 2));
  1113. $ids[] = $DB->insert_record($tablename, array('course' => 1));
  1114. $ids[] = $DB->insert_record($tablename, array('course' => 0));
  1115. $rs = $DB->export_table_recordset($tablename);
  1116. $rids = array();
  1117. foreach ($rs as $record) {
  1118. $rids[] = $record->id;
  1119. }
  1120. $rs->close();
  1121. $this->assertEquals($ids, $rids, '', 0, 0, true);
  1122. }
  1123. public function test_get_records() {
  1124. $DB = $this->tdb;
  1125. $dbman = $DB->get_manager();
  1126. $table = $this->get_test_table();
  1127. $tablename = $table->getName();
  1128. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1129. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1130. $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
  1131. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1132. $dbman->create_table($table);
  1133. $DB->insert_record($tablename, array('course' => 3));
  1134. $DB->insert_record($tablename, array('course' => 3));
  1135. $DB->insert_record($tablename, array('course' => 5));
  1136. $DB->insert_record($tablename, array('course' => 2));
  1137. // All records.
  1138. $records = $DB->get_records($tablename);
  1139. $this->assertCount(4, $records);
  1140. $this->assertEquals(3, $records[1]->course);
  1141. $this->assertEquals(3, $records[2]->course);
  1142. $this->assertEquals(5, $records[3]->course);
  1143. $this->assertEquals(2, $records[4]->course);
  1144. // Records matching certain conditions.
  1145. $records = $DB->get_records($tablename, array('course' => 3));
  1146. $this->assertCount(2, $records);
  1147. $this->assertEquals(3, $records[1]->course);
  1148. $this->assertEquals(3, $records[2]->course);
  1149. // All records sorted by course.
  1150. $records = $DB->get_records($tablename, null, 'course');
  1151. $this->assertCount(4, $records);
  1152. $current_record = reset($records);
  1153. $this->assertEquals(4, $current_record->id);
  1154. $current_record = next($records);
  1155. $this->assertEquals(1, $current_record->id);
  1156. $current_record = next($records);
  1157. $this->assertEquals(2, $current_record->id);
  1158. $current_record = next($records);
  1159. $this->assertEquals(3, $current_record->id);
  1160. // All records, but get only one field.
  1161. $records = $DB->get_records($tablename, null, '', 'id');
  1162. $this->assertFalse(isset($records[1]->course));
  1163. $this->assertTrue(isset($records[1]->id));
  1164. $this->assertCount(4, $records);
  1165. // Booleans into params.
  1166. $records = $DB->get_records($tablename, array('course' => true));
  1167. $this->assertCount(0, $records);
  1168. $records = $DB->get_records($tablename, array('course' => false));
  1169. $this->assertCount(0, $records);
  1170. // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
  1171. $conditions = array('onetext' => '1');
  1172. try {
  1173. $records = $DB->get_records($tablename, $conditions);
  1174. if (debugging()) {
  1175. // Only in debug mode - hopefully all devs test code in debug mode...
  1176. $this->fail('An Exception is missing, expected due to equating of text fields');
  1177. }
  1178. } catch (moodle_exception $e) {
  1179. $this->assertInstanceOf('dml_exception', $e);
  1180. $this->assertSame('textconditionsnotallowed', $e->errorcode);
  1181. }
  1182. // Test get_records passing non-existing table.
  1183. // with params.
  1184. try {
  1185. $records = $DB->get_records('xxxx', array('id' => 0));
  1186. $this->fail('An Exception is missing, expected due to query against non-existing table');
  1187. } catch (moodle_exception $e) {
  1188. $this->assertInstanceOf('dml_exception', $e);
  1189. if (debugging()) {
  1190. // Information for developers only, normal users get general error message.
  1191. $this->assertSame('ddltablenotexist', $e->errorcode);
  1192. }
  1193. }
  1194. // And without params.
  1195. try {
  1196. $records = $DB->get_records('xxxx', array());
  1197. $this->fail('An Exception is missing, expected due to query against non-existing table');
  1198. } catch (moodle_exception $e) {
  1199. $this->assertInstanceOf('dml_exception', $e);
  1200. if (debugging()) {
  1201. // Information for developers only, normal users get general error message.
  1202. $this->assertSame('ddltablenotexist', $e->errorcode);
  1203. }
  1204. }
  1205. // Test get_records passing non-existing column.
  1206. try {
  1207. $records = $DB->get_records($tablename, array('xxxx' => 0));
  1208. $this->fail('An Exception is missing, expected due to query against non-existing column');
  1209. } catch (moodle_exception $e) {
  1210. $this->assertInstanceOf('dml_exception', $e);
  1211. if (debugging()) {
  1212. // Information for developers only, normal users get general error message.
  1213. $this->assertSame('ddlfieldnotexist', $e->errorcode);
  1214. }
  1215. }
  1216. // Note: delegate limits testing to test_get_records_sql().
  1217. }
  1218. public function test_get_records_list() {
  1219. $DB = $this->tdb;
  1220. $dbman = $DB->get_manager();
  1221. $table = $this->get_test_table();
  1222. $tablename = $table->getName();
  1223. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1224. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1225. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1226. $dbman->create_table($table);
  1227. $DB->insert_record($tablename, array('course' => 3));
  1228. $DB->insert_record($tablename, array('course' => 3));
  1229. $DB->insert_record($tablename, array('course' => 5));
  1230. $DB->insert_record($tablename, array('course' => 2));
  1231. $records = $DB->get_records_list($tablename, 'course', array(3, 2));
  1232. $this->assertInternalType('array', $records);
  1233. $this->assertCount(3, $records);
  1234. $this->assertEquals(1, reset($records)->id);
  1235. $this->assertEquals(2, next($records)->id);
  1236. $this->assertEquals(4, next($records)->id);
  1237. $this->assertSame(array(), $records = $DB->get_records_list($tablename, 'course', array())); // Must return 0 rows without conditions. MDL-17645.
  1238. $this->assertCount(0, $records);
  1239. // Note: delegate limits testing to test_get_records_sql().
  1240. }
  1241. public function test_get_records_sql() {
  1242. $DB = $this->tdb;
  1243. $dbman = $DB->get_manager();
  1244. $table = $this->get_test_table();
  1245. $tablename = $table->getName();
  1246. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1247. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1248. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1249. $dbman->create_table($table);
  1250. $inskey1 = $DB->insert_record($tablename, array('course' => 3));
  1251. $inskey2 = $DB->insert_record($tablename, array('course' => 5));
  1252. $inskey3 = $DB->insert_record($tablename, array('course' => 4));
  1253. $inskey4 = $DB->insert_record($tablename, array('course' => 3));
  1254. $inskey5 = $DB->insert_record($tablename, array('course' => 2));
  1255. $inskey6 = $DB->insert_record($tablename, array('course' => 1));
  1256. $inskey7 = $DB->insert_record($tablename, array('course' => 0));
  1257. $table2 = $this->get_test_table("2");
  1258. $tablename2 = $table2->getName();
  1259. $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1260. $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1261. $table2->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
  1262. $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1263. $dbman->create_table($table2);
  1264. $DB->insert_record($tablename2, array('course'=>3, 'nametext'=>'badabing'));
  1265. $DB->insert_record($tablename2, array('course'=>4, 'nametext'=>'badabang'));
  1266. $DB->insert_record($tablename2, array('course'=>5, 'nametext'=>'badabung'));
  1267. $DB->insert_record($tablename2, array('course'=>6, 'nametext'=>'badabong'));
  1268. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
  1269. $this->assertCount(2, $records);
  1270. $this->assertEquals($inskey1, reset($records)->id);
  1271. $this->assertEquals($inskey4, next($records)->id);
  1272. // Awful test, requires debug enabled and sent to browser. Let's do that and restore after test.
  1273. $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null);
  1274. $this->assertDebuggingCalled();
  1275. $this->assertCount(6, $records);
  1276. set_debugging(DEBUG_MINIMAL);
  1277. $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null);
  1278. $this->assertDebuggingNotCalled();
  1279. $this->assertCount(6, $records);
  1280. set_debugging(DEBUG_DEVELOPER);
  1281. // Negative limits = no limits.
  1282. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, -1, -1);
  1283. $this->assertCount(7, $records);
  1284. // Zero limits = no limits.
  1285. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 0);
  1286. $this->assertCount(7, $records);
  1287. // Only limitfrom = skips that number of records.
  1288. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
  1289. $this->assertCount(5, $records);
  1290. $this->assertEquals($inskey3, reset($records)->id);
  1291. $this->assertEquals($inskey7, end($records)->id);
  1292. // Only limitnum = fetches that number of records.
  1293. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 3);
  1294. $this->assertCount(3, $records);
  1295. $this->assertEquals($inskey1, reset($records)->id);
  1296. $this->assertEquals($inskey3, end($records)->id);
  1297. // Both limitfrom and limitnum = skips limitfrom records and fetches limitnum ones.
  1298. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 3, 2);
  1299. $this->assertCount(2, $records);
  1300. $this->assertEquals($inskey4, reset($records)->id);
  1301. $this->assertEquals($inskey5, end($records)->id);
  1302. // Both limitfrom and limitnum in query having subqueris.
  1303. // Note the subquery skips records with course = 0 and 3.
  1304. $sql = "SELECT * FROM {{$tablename}}
  1305. WHERE course NOT IN (
  1306. SELECT course FROM {{$tablename}}
  1307. WHERE course IN (0, 3))
  1308. ORDER BY course";
  1309. $records = $DB->get_records_sql($sql, null, 0, 2); // Skip 0, get 2.
  1310. $this->assertCount(2, $records);
  1311. $this->assertEquals($inskey6, reset($records)->id);
  1312. $this->assertEquals($inskey5, end($records)->id);
  1313. $records = $DB->get_records_sql($sql, null, 2, 2); // Skip 2, get 2.
  1314. $this->assertCount(2, $records);
  1315. $this->assertEquals($inskey3, reset($records)->id);
  1316. $this->assertEquals($inskey2, end($records)->id);
  1317. // Test 2 tables with aliases and limits with order bys.
  1318. $sql = "SELECT t1.id, t1.course AS cid, t2.nametext
  1319. FROM {{$tablename}} t1, {{$tablename2}} t2
  1320. WHERE t2.course=t1.course
  1321. ORDER BY t1.course, ". $DB->sql_compare_text('t2.nametext');
  1322. $records = $DB->get_records_sql($sql, null, 2, 2); // Skip courses 3 and 6, get 4 and 5.
  1323. $this->assertCount(2, $records);
  1324. $this->assertSame('5', end($records)->cid);
  1325. $this->assertSame('4', reset($records)->cid);
  1326. // Test 2 tables with aliases and limits with the highest INT limit works.
  1327. $records = $DB->get_records_sql($sql, null, 2, PHP_INT_MAX); // Skip course {3,6}, get {4,5}.
  1328. $this->assertCount(2, $records);
  1329. $this->assertSame('5', end($records)->cid);
  1330. $this->assertSame('4', reset($records)->cid);
  1331. // Test 2 tables with aliases and limits with order bys (limit which is highest INT number).
  1332. $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, 2); // Skip all courses.
  1333. $this->assertCount(0, $records);
  1334. // Test 2 tables with aliases and limits with order bys (limit which s highest INT number).
  1335. $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, PHP_INT_MAX); // Skip all courses.
  1336. $this->assertCount(0, $records);
  1337. // TODO: Test limits in queries having DISTINCT clauses.
  1338. // Note: fetching nulls, empties, LOBs already tested by test_update_record() no needed here.
  1339. }
  1340. public function test_get_records_menu() {
  1341. $DB = $this->tdb;
  1342. $dbman = $DB->get_manager();
  1343. $table = $this->get_test_table();
  1344. $tablename = $table->getName();
  1345. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1346. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1347. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1348. $dbman->create_table($table);
  1349. $DB->insert_record($tablename, array('course' => 3));
  1350. $DB->insert_record($tablename, array('course' => 3));
  1351. $DB->insert_record($tablename, array('course' => 5));
  1352. $DB->insert_record($tablename, array('course' => 2));
  1353. $records = $DB->get_records_menu($tablename, array('course' => 3));
  1354. $this->assertInternalType('array', $records);
  1355. $this->assertCount(2, $records);
  1356. $this->assertNotEmpty($records[1]);
  1357. $this->assertNotEmpty($records[2]);
  1358. $this->assertEquals(3, $records[1]);
  1359. $this->assertEquals(3, $records[2]);
  1360. // Note: delegate limits testing to test_get_records_sql().
  1361. }
  1362. public function test_get_records_select_menu() {
  1363. $DB = $this->tdb;
  1364. $dbman = $DB->get_manager();
  1365. $table = $this->get_test_table();
  1366. $tablename = $table->getName();
  1367. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1368. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1369. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1370. $dbman->create_table($table);
  1371. $DB->insert_record($tablename, array('course' => 3));
  1372. $DB->insert_record($tablename, array('course' => 2));
  1373. $DB->insert_record($tablename, array('course' => 3));
  1374. $DB->insert_record($tablename, array('course' => 5));
  1375. $records = $DB->get_records_select_menu($tablename, "course > ?", array(2));
  1376. $this->assertInternalType('array', $records);
  1377. $this->assertCount(3, $records);
  1378. $this->assertArrayHasKey(1, $records);
  1379. $this->assertArrayNotHasKey(2, $records);
  1380. $this->assertArrayHasKey(3, $records);
  1381. $this->assertArrayHasKey(4, $records);
  1382. $this->assertSame('3', $records[1]);
  1383. $this->assertSame('3', $records[3]);
  1384. $this->assertSame('5', $records[4]);
  1385. // Note: delegate limits testing to test_get_records_sql().
  1386. }
  1387. public function test_get_records_sql_menu() {
  1388. $DB = $this->tdb;
  1389. $dbman = $DB->get_manager();
  1390. $table = $this->get_test_table();
  1391. $tablename = $table->getName();
  1392. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1393. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1394. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1395. $dbman->create_table($table);
  1396. $DB->insert_record($tablename, array('course' => 3));
  1397. $DB->insert_record($tablename, array('course' => 2));
  1398. $DB->insert_record($tablename, array('course' => 3));
  1399. $DB->insert_record($tablename, array('course' => 5));
  1400. $records = $DB->get_records_sql_menu("SELECT * FROM {{$tablename}} WHERE course > ?", array(2));
  1401. $this->assertInternalType('array', $records);
  1402. $this->assertCount(3, $records);
  1403. $this->assertArrayHasKey(1, $records);
  1404. $this->assertArrayNotHasKey(2, $records);
  1405. $this->assertArrayHasKey(3, $records);
  1406. $this->assertArrayHasKey(4, $records);
  1407. $this->assertSame('3', $records[1]);
  1408. $this->assertSame('3', $records[3]);
  1409. $this->assertSame('5', $records[4]);
  1410. // Note: delegate limits testing to test_get_records_sql().
  1411. }
  1412. public function test_get_record() {
  1413. $DB = $this->tdb;
  1414. $dbman = $DB->get_manager();
  1415. $table = $this->get_test_table();
  1416. $tablename = $table->getName();
  1417. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1418. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1419. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1420. $dbman->create_table($table);
  1421. $DB->insert_record($tablename, array('course' => 3));
  1422. $DB->insert_record($tablename, array('course' => 2));
  1423. $record = $DB->get_record($tablename, array('id' => 2));
  1424. $this->assertInstanceOf('stdClass', $record);
  1425. $this->assertEquals(2, $record->course);
  1426. $this->assertEquals(2, $record->id);
  1427. }
  1428. public function test_get_record_select() {
  1429. $DB = $this->tdb;
  1430. $dbman = $DB->get_manager();
  1431. $table = $this->get_test_table();
  1432. $tablename = $table->getName();
  1433. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1434. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1435. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1436. $dbman->create_table($table);
  1437. $DB->insert_record($tablename, array('course' => 3));
  1438. $DB->insert_record($tablename, array('course' => 2));
  1439. $record = $DB->get_record_select($tablename, "id = ?", array(2));
  1440. $this->assertInstanceOf('stdClass', $record);
  1441. $this->assertEquals(2, $record->course);
  1442. // Note: delegates limit testing to test_get_records_sql().
  1443. }
  1444. public function test_get_record_sql() {
  1445. $DB = $this->tdb;
  1446. $dbman = $DB->get_manager();
  1447. $table = $this->get_test_table();
  1448. $tablename = $table->getName();
  1449. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1450. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1451. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1452. $dbman->create_table($table);
  1453. $DB->insert_record($tablename, array('course' => 3));
  1454. $DB->insert_record($tablename, array('course' => 2));
  1455. // Standard use.
  1456. $record = $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(2));
  1457. $this->assertInstanceOf('stdClass', $record);
  1458. $this->assertEquals(2, $record->course);
  1459. $this->assertEquals(2, $record->id);
  1460. // Backwards compatibility with $ignoremultiple.
  1461. $this->assertFalse((bool)IGNORE_MISSING);
  1462. $this->assertTrue((bool)IGNORE_MULTIPLE);
  1463. // Record not found - ignore.
  1464. $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MISSING));
  1465. $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MULTIPLE));
  1466. // Record not found error.
  1467. try {
  1468. $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), MUST_EXIST);
  1469. $this->fail("Exception expected");
  1470. } catch (dml_missing_record_exception $e) {
  1471. $this->assertTrue(true);
  1472. }
  1473. $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING));
  1474. $this->assertDebuggingCalled();
  1475. set_debugging(DEBUG_MINIMAL);
  1476. $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING));
  1477. $this->assertDebuggingNotCalled();
  1478. set_debugging(DEBUG_DEVELOPER);
  1479. // Multiple matches ignored.
  1480. $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MULTIPLE));
  1481. // Multiple found error.
  1482. try {
  1483. $DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), MUST_EXIST);
  1484. $this->fail("Exception expected");
  1485. } catch (dml_multiple_records_exception $e) {
  1486. $this->assertTrue(true);
  1487. }
  1488. }
  1489. public function test_get_field() {
  1490. $DB = $this->tdb;
  1491. $dbman = $DB->get_manager();
  1492. $table = $this->get_test_table();
  1493. $tablename = $table->getName();
  1494. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1495. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1496. $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
  1497. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1498. $dbman->create_table($table);
  1499. $id1 = $DB->insert_record($tablename, array('course' => 3));
  1500. $DB->insert_record($tablename, array('course' => 5));
  1501. $DB->insert_record($tablename, array('course' => 5));
  1502. $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id1)));
  1503. $this->assertEquals(3, $DB->get_field($tablename, 'course', array('course' => 3)));
  1504. $this->assertFalse($DB->get_field($tablename, 'course', array('course' => 11), IGNORE_MISSING));
  1505. try {
  1506. $DB->get_field($tablename, 'course', array('course' => 4), MUST_EXIST);
  1507. $this->fail('Exception expected due to missing record');
  1508. } catch (dml_exception $ex) {
  1509. $this->assertTrue(true);
  1510. }
  1511. $this->assertEquals(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MULTIPLE));
  1512. $this->assertDebuggingNotCalled();
  1513. $this->assertEquals(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MISSING));
  1514. $this->assertDebuggingCalled();
  1515. // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
  1516. $conditions = array('onetext' => '1');
  1517. try {
  1518. $DB->get_field($tablename, 'course', $conditions);
  1519. if (debugging()) {
  1520. // Only in debug mode - hopefully all devs test code in debug mode...
  1521. $this->fail('An Exception is missing, expected due to equating of text fields');
  1522. }
  1523. } catch (moodle_exception $e) {
  1524. $this->assertInstanceOf('dml_exception', $e);
  1525. $this->assertSame('textconditionsnotallowed', $e->errorcode);
  1526. }
  1527. }
  1528. public function test_get_field_select() {
  1529. $DB = $this->tdb;
  1530. $dbman = $DB->get_manager();
  1531. $table = $this->get_test_table();
  1532. $tablename = $table->getName();
  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. $DB->insert_record($tablename, array('course' => 3));
  1538. $this->assertEquals(3, $DB->get_field_select($tablename, 'course', "id = ?", array(1)));
  1539. }
  1540. public function test_get_field_sql() {
  1541. $DB = $this->tdb;
  1542. $dbman = $DB->get_manager();
  1543. $table = $this->get_test_table();
  1544. $tablename = $table->getName();
  1545. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1546. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1547. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1548. $dbman->create_table($table);
  1549. $DB->insert_record($tablename, array('course' => 3));
  1550. $this->assertEquals(3, $DB->get_field_sql("SELECT course FROM {{$tablename}} WHERE id = ?", array(1)));
  1551. }
  1552. public function test_get_fieldset_select() {
  1553. $DB = $this->tdb;
  1554. $dbman = $DB->get_manager();
  1555. $table = $this->get_test_table();
  1556. $tablename = $table->getName();
  1557. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1558. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1559. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1560. $dbman->create_table($table);
  1561. $DB->insert_record($tablename, array('course' => 1));
  1562. $DB->insert_record($tablename, array('course' => 3));
  1563. $DB->insert_record($tablename, array('course' => 2));
  1564. $DB->insert_record($tablename, array('course' => 6));
  1565. $fieldset = $DB->get_fieldset_select($tablename, 'course', "course > ?", array(1));
  1566. $this->assertInternalType('array', $fieldset);
  1567. $this->assertCount(3, $fieldset);
  1568. $this->assertEquals(3, $fieldset[0]);
  1569. $this->assertEquals(2, $fieldset[1]);
  1570. $this->assertEquals(6, $fieldset[2]);
  1571. }
  1572. public function test_get_fieldset_sql() {
  1573. $DB = $this->tdb;
  1574. $dbman = $DB->get_manager();
  1575. $table = $this->get_test_table();
  1576. $tablename = $table->getName();
  1577. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1578. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1579. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1580. $dbman->create_table($table);
  1581. $DB->insert_record($tablename, array('course' => 1));
  1582. $DB->insert_record($tablename, array('course' => 3));
  1583. $DB->insert_record($tablename, array('course' => 2));
  1584. $DB->insert_record($tablename, array('course' => 6));
  1585. $fieldset = $DB->get_fieldset_sql("SELECT * FROM {{$tablename}} WHERE course > ?", array(1));
  1586. $this->assertInternalType('array', $fieldset);
  1587. $this->assertCount(3, $fieldset);
  1588. $this->assertEquals(2, $fieldset[0]);
  1589. $this->assertEquals(3, $fieldset[1]);
  1590. $this->assertEquals(4, $fieldset[2]);
  1591. }
  1592. public function test_insert_record_raw() {
  1593. $DB = $this->tdb;
  1594. $dbman = $DB->get_manager();
  1595. $table = $this->get_test_table();
  1596. $tablename = $table->getName();
  1597. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1598. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1599. $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
  1600. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1601. $dbman->create_table($table);
  1602. $record = (object)array('course' => 1, 'onechar' => 'xx');
  1603. $before = clone($record);
  1604. $result = $DB->insert_record_raw($tablename, $record);
  1605. $this->assertSame(1, $result);
  1606. $this->assertEquals($record, $before);
  1607. $record = $DB->get_record($tablename, array('course' => 1));
  1608. $this->assertInstanceOf('stdClass', $record);
  1609. $this->assertSame('xx', $record->onechar);
  1610. $result = $DB->insert_record_raw($tablename, array('course' => 2, 'onechar' => 'yy'), false);
  1611. $this->assertTrue($result);
  1612. // Note: bulk not implemented yet.
  1613. $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'zz'), true, true);
  1614. $record = $DB->get_record($tablename, array('course' => 3));
  1615. $this->assertInstanceOf('stdClass', $record);
  1616. $this->assertSame('zz', $record->onechar);
  1617. // Custom sequence (id) - returnid is ignored.
  1618. $result = $DB->insert_record_raw($tablename, array('id' => 10, 'course' => 3, 'onechar' => 'bb'), true, false, true);
  1619. $this->assertTrue($result);
  1620. $record = $DB->get_record($tablename, array('id' => 10));
  1621. $this->assertInstanceOf('stdClass', $record);
  1622. $this->assertSame('bb', $record->onechar);
  1623. // Custom sequence - missing id error.
  1624. try {
  1625. $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'bb'), true, false, true);
  1626. $this->fail('Exception expected due to missing record');
  1627. } catch (coding_exception $ex) {
  1628. $this->assertTrue(true);
  1629. }
  1630. // Wrong column error.
  1631. try {
  1632. $DB->insert_record_raw($tablename, array('xxxxx' => 3, 'onechar' => 'bb'));
  1633. $this->fail('Exception expected due to invalid column');
  1634. } catch (dml_exception $ex) {
  1635. $this->assertTrue(true);
  1636. }
  1637. // Create something similar to "context_temp" with id column without sequence.
  1638. $dbman->drop_table($table);
  1639. $table = $this->get_test_table();
  1640. $tablename = $table->getName();
  1641. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
  1642. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1643. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1644. $dbman->create_table($table);
  1645. $record = (object)array('id'=>5, 'course' => 1);
  1646. $DB->insert_record_raw($tablename, $record, false, false, true);
  1647. $record = $DB->get_record($tablename, array());
  1648. $this->assertEquals(5, $record->id);
  1649. }
  1650. public function test_insert_record() {
  1651. // All the information in this test is fetched from DB by get_recordset() so we
  1652. // have such method properly tested against nulls, empties and friends...
  1653. $DB = $this->tdb;
  1654. $dbman = $DB->get_manager();
  1655. $table = $this->get_test_table();
  1656. $tablename = $table->getName();
  1657. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1658. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1659. $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, null, null, 100);
  1660. $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
  1661. $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
  1662. $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
  1663. $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
  1664. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1665. $dbman->create_table($table);
  1666. $this->assertSame(1, $DB->insert_record($tablename, array('course' => 1), true));
  1667. $record = $DB->get_record($tablename, array('course' => 1));
  1668. $this->assertEquals(1, $record->id);
  1669. $this->assertEquals(100, $record->oneint); // Just check column defaults have been applied.
  1670. $this->assertEquals(200, $record->onenum);
  1671. $this->assertSame('onestring', $record->onechar);
  1672. $this->assertNull($record->onetext);
  1673. $this->assertNull($record->onebinary);
  1674. // Without returning id, bulk not implemented.
  1675. $result = $this->assertTrue($DB->insert_record($tablename, array('course' => 99), false, true));
  1676. $record = $DB->get_record($tablename, array('course' => 99));
  1677. $this->assertEquals(2, $record->id);
  1678. $this->assertEquals(99, $record->course);
  1679. // Check nulls are set properly for all types.
  1680. $record = new stdClass();
  1681. $record->oneint = null;
  1682. $record->onenum = null;
  1683. $record->onechar = null;
  1684. $record->onetext = null;
  1685. $record->onebinary = null;
  1686. $recid = $DB->insert_record($tablename, $record);
  1687. $record = $DB->get_record($tablename, array('id' => $recid));
  1688. $this->assertEquals(0, $record->course);
  1689. $this->assertNull($record->oneint);
  1690. $this->assertNull($record->onenum);
  1691. $this->assertNull($record->onechar);
  1692. $this->assertNull($record->onetext);
  1693. $this->assertNull($record->onebinary);
  1694. // Check zeros are set properly for all types.
  1695. $record = new stdClass();
  1696. $record->oneint = 0;
  1697. $record->onenum = 0;
  1698. $recid = $DB->insert_record($tablename, $record);
  1699. $record = $DB->get_record($tablename, array('id' => $recid));
  1700. $this->assertEquals(0, $record->oneint);
  1701. $this->assertEquals(0, $record->onenum);
  1702. // Check booleans are set properly for all types.
  1703. $record = new stdClass();
  1704. $record->oneint = true; // Trues.
  1705. $record->onenum = true;
  1706. $record->onechar = true;
  1707. $record->onetext = true;
  1708. $recid = $DB->insert_record($tablename, $record);
  1709. $record = $DB->get_record($tablename, array('id' => $recid));
  1710. $this->assertEquals(1, $record->oneint);
  1711. $this->assertEquals(1, $record->onenum);
  1712. $this->assertEquals(1, $record->onechar);
  1713. $this->assertEquals(1, $record->onetext);
  1714. $record = new stdClass();
  1715. $record->oneint = false; // Falses.
  1716. $record->onenum = false;
  1717. $record->onechar = false;
  1718. $record->onetext = false;
  1719. $recid = $DB->insert_record($tablename, $record);
  1720. $record = $DB->get_record($tablename, array('id' => $recid));
  1721. $this->assertEquals(0, $record->oneint);
  1722. $this->assertEquals(0, $record->onenum);
  1723. $this->assertEquals(0, $record->onechar);
  1724. $this->assertEquals(0, $record->onetext);
  1725. // Check string data causes exception in numeric types.
  1726. $record = new stdClass();
  1727. $record->oneint = 'onestring';
  1728. $record->onenum = 0;
  1729. try {
  1730. $DB->insert_record($tablename, $record);
  1731. $this->fail("Expecting an exception, none occurred");
  1732. } catch (moodle_exception $e) {
  1733. $this->assertInstanceOf('dml_exception', $e);
  1734. }
  1735. $record = new stdClass();
  1736. $record->oneint = 0;
  1737. $record->onenum = 'onestring';
  1738. try {
  1739. $DB->insert_record($tablename, $record);
  1740. $this->fail("Expecting an exception, none occurred");
  1741. } catch (moodle_exception $e) {
  1742. $this->assertInstanceOf('dml_exception', $e);
  1743. }
  1744. // Check empty string data is stored as 0 in numeric datatypes.
  1745. $record = new stdClass();
  1746. $record->oneint = ''; // Empty string.
  1747. $record->onenum = 0;
  1748. $recid = $DB->insert_record($tablename, $record);
  1749. $record = $DB->get_record($tablename, array('id' => $recid));
  1750. $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
  1751. $record = new stdClass();
  1752. $record->oneint = 0;
  1753. $record->onenum = ''; // Empty string.
  1754. $recid = $DB->insert_record($tablename, $record);
  1755. $record = $DB->get_record($tablename, array('id' => $recid));
  1756. $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
  1757. // Check empty strings are set properly in string types.
  1758. $record = new stdClass();
  1759. $record->oneint = 0;
  1760. $record->onenum = 0;
  1761. $record->onechar = '';
  1762. $record->onetext = '';
  1763. $recid = $DB->insert_record($tablename, $record);
  1764. $record = $DB->get_record($tablename, array('id' => $recid));
  1765. $this->assertTrue($record->onechar === '');
  1766. $this->assertTrue($record->onetext === '');
  1767. // Check operation ((210.10 + 39.92) - 150.02) against numeric types.
  1768. $record = new stdClass();
  1769. $record->oneint = ((210.10 + 39.92) - 150.02);
  1770. $record->onenum = ((210.10 + 39.92) - 150.02);
  1771. $recid = $DB->insert_record($tablename, $record);
  1772. $record = $DB->get_record($tablename, array('id' => $recid));
  1773. $this->assertEquals(100, $record->oneint);
  1774. $this->assertEquals(100, $record->onenum);
  1775. // Check various quotes/backslashes combinations in string types.
  1776. $teststrings = array(
  1777. 'backslashes and quotes alone (even): "" \'\' \\\\',
  1778. 'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
  1779. 'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
  1780. 'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
  1781. foreach ($teststrings as $teststring) {
  1782. $record = new stdClass();
  1783. $record->onechar = $teststring;
  1784. $record->onetext = $teststring;
  1785. $recid = $DB->insert_record($tablename, $record);
  1786. $record = $DB->get_record($tablename, array('id' => $recid));
  1787. $this->assertEquals($teststring, $record->onechar);
  1788. $this->assertEquals($teststring, $record->onetext);
  1789. }
  1790. // Check LOBs in text/binary columns.
  1791. $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
  1792. $blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
  1793. $record = new stdClass();
  1794. $record->onetext = $clob;
  1795. $record->onebinary = $blob;
  1796. $recid = $DB->insert_record($tablename, $record);
  1797. $rs = $DB->get_recordset($tablename, array('id' => $recid));
  1798. $record = $rs->current();
  1799. $rs->close();
  1800. $this->assertEquals($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
  1801. $this->assertEquals($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
  1802. // And "small" LOBs too, just in case.
  1803. $newclob = substr($clob, 0, 500);
  1804. $newblob = substr($blob, 0, 250);
  1805. $record = new stdClass();
  1806. $record->onetext = $newclob;
  1807. $record->onebinary = $newblob;
  1808. $recid = $DB->insert_record($tablename, $record);
  1809. $rs = $DB->get_recordset($tablename, array('id' => $recid));
  1810. $record = $rs->current();
  1811. $rs->close();
  1812. $this->assertEquals($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
  1813. $this->assertEquals($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
  1814. $this->assertEquals(false, $rs->key()); // Ensure recordset key() method to be working ok after closing.
  1815. // And "diagnostic" LOBs too, just in case.
  1816. $newclob = '\'"\\;/ěščřžýáíé';
  1817. $newblob = '\'"\\;/ěščřžýáíé';
  1818. $record = new stdClass();
  1819. $record->onetext = $newclob;
  1820. $record->onebinary = $newblob;
  1821. $recid = $DB->insert_record($tablename, $record);
  1822. $rs = $DB->get_recordset($tablename, array('id' => $recid));
  1823. $record = $rs->current();
  1824. $rs->close();
  1825. $this->assertSame($newclob, $record->onetext);
  1826. $this->assertSame($newblob, $record->onebinary);
  1827. $this->assertEquals(false, $rs->key()); // Ensure recordset key() method to be working ok after closing.
  1828. // Test data is not modified.
  1829. $record = new stdClass();
  1830. $record->id = -1; // Has to be ignored.
  1831. $record->course = 3;
  1832. $record->lalala = 'lalal'; // Unused.
  1833. $before = clone($record);
  1834. $DB->insert_record($tablename, $record);
  1835. $this->assertEquals($record, $before);
  1836. // Make sure the id is always increasing and never reuses the same id.
  1837. $id1 = $DB->insert_record($tablename, array('course' => 3));
  1838. $id2 = $DB->insert_record($tablename, array('course' => 3));
  1839. $this->assertTrue($id1 < $id2);
  1840. $DB->delete_records($tablename, array('id'=>$id2));
  1841. $id3 = $DB->insert_record($tablename, array('course' => 3));
  1842. $this->assertTrue($id2 < $id3);
  1843. $DB->delete_records($tablename, array());
  1844. $id4 = $DB->insert_record($tablename, array('course' => 3));
  1845. $this->assertTrue($id3 < $id4);
  1846. // Test saving a float in a CHAR column, and reading it back.
  1847. $id = $DB->insert_record($tablename, array('onechar' => 1.0));
  1848. $this->assertEquals(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  1849. $id = $DB->insert_record($tablename, array('onechar' => 1e20));
  1850. $this->assertEquals(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  1851. $id = $DB->insert_record($tablename, array('onechar' => 1e-4));
  1852. $this->assertEquals(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  1853. $id = $DB->insert_record($tablename, array('onechar' => 1e-5));
  1854. $this->assertEquals(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  1855. $id = $DB->insert_record($tablename, array('onechar' => 1e-300));
  1856. $this->assertEquals(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  1857. $id = $DB->insert_record($tablename, array('onechar' => 1e300));
  1858. $this->assertEquals(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  1859. // Test saving a float in a TEXT column, and reading it back.
  1860. $id = $DB->insert_record($tablename, array('onetext' => 1.0));
  1861. $this->assertEquals(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  1862. $id = $DB->insert_record($tablename, array('onetext' => 1e20));
  1863. $this->assertEquals(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  1864. $id = $DB->insert_record($tablename, array('onetext' => 1e-4));
  1865. $this->assertEquals(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  1866. $id = $DB->insert_record($tablename, array('onetext' => 1e-5));
  1867. $this->assertEquals(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  1868. $id = $DB->insert_record($tablename, array('onetext' => 1e-300));
  1869. $this->assertEquals(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  1870. $id = $DB->insert_record($tablename, array('onetext' => 1e300));
  1871. $this->assertEquals(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  1872. // Test that inserting data violating one unique key leads to error.
  1873. // Empty the table completely.
  1874. $this->assertTrue($DB->delete_records($tablename));
  1875. // Add one unique constraint (index).
  1876. $key = new xmldb_key('testuk', XMLDB_KEY_UNIQUE, array('course', 'oneint'));
  1877. $dbman->add_key($table, $key);
  1878. // Let's insert one record violating the constraint multiple times.
  1879. $record = (object)array('course' => 1, 'oneint' => 1);
  1880. $this->assertTrue($DB->insert_record($tablename, $record, false)); // Insert 1st. No problem expected.
  1881. // Re-insert same record, not returning id. dml_exception expected.
  1882. try {
  1883. $DB->insert_record($tablename, $record, false);
  1884. $this->fail("Expecting an exception, none occurred");
  1885. } catch (moodle_exception $e) {
  1886. $this->assertInstanceOf('dml_exception', $e);
  1887. }
  1888. // Re-insert same record, returning id. dml_exception expected.
  1889. try {
  1890. $DB->insert_record($tablename, $record, true);
  1891. $this->fail("Expecting an exception, none occurred");
  1892. } catch (moodle_exception $e) {
  1893. $this->assertInstanceOf('dml_exception', $e);
  1894. }
  1895. }
  1896. public function test_import_record() {
  1897. // All the information in this test is fetched from DB by get_recordset() so we
  1898. // have such method properly tested against nulls, empties and friends...
  1899. $DB = $this->tdb;
  1900. $dbman = $DB->get_manager();
  1901. $table = $this->get_test_table();
  1902. $tablename = $table->getName();
  1903. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  1904. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  1905. $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, null, null, 100);
  1906. $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
  1907. $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
  1908. $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
  1909. $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
  1910. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  1911. $dbman->create_table($table);
  1912. $this->assertSame(1, $DB->insert_record($tablename, array('course' => 1), true));
  1913. $record = $DB->get_record($tablename, array('course' => 1));
  1914. $this->assertEquals(1, $record->id);
  1915. $this->assertEquals(100, $record->oneint); // Just check column defaults have been applied.
  1916. $this->assertEquals(200, $record->onenum);
  1917. $this->assertSame('onestring', $record->onechar);
  1918. $this->assertNull($record->onetext);
  1919. $this->assertNull($record->onebinary);
  1920. // Ignore extra columns.
  1921. $record = (object)array('id'=>13, 'course'=>2, 'xxxx'=>788778);
  1922. $before = clone($record);
  1923. $this->assertTrue($DB->import_record($tablename, $record));
  1924. $this->assertEquals($record, $before);
  1925. $records = $DB->get_records($tablename);
  1926. $this->assertEquals(2, $records[13]->course);
  1927. // Check nulls are set properly for all types.
  1928. $record = new stdClass();
  1929. $record->id = 20;
  1930. $record->oneint = null;
  1931. $record->onenum = null;
  1932. $record->onechar = null;
  1933. $record->onetext = null;
  1934. $record->onebinary = null;
  1935. $this->assertTrue($DB->import_record($tablename, $record));
  1936. $record = $DB->get_record($tablename, array('id' => 20));
  1937. $this->assertEquals(0, $record->course);
  1938. $this->assertNull($record->oneint);
  1939. $this->assertNull($record->onenum);
  1940. $this->assertNull($record->onechar);
  1941. $this->assertNull($record->onetext);
  1942. $this->assertNull($record->onebinary);
  1943. // Check zeros are set properly for all types.
  1944. $record = new stdClass();
  1945. $record->id = 23;
  1946. $record->oneint = 0;
  1947. $record->onenum = 0;
  1948. $this->assertTrue($DB->import_record($tablename, $record));
  1949. $record = $DB->get_record($tablename, array('id' => 23));
  1950. $this->assertEquals(0, $record->oneint);
  1951. $this->assertEquals(0, $record->onenum);
  1952. // Check string data causes exception in numeric types.
  1953. $record = new stdClass();
  1954. $record->id = 32;
  1955. $record->oneint = 'onestring';
  1956. $record->onenum = 0;
  1957. try {
  1958. $DB->import_record($tablename, $record);
  1959. $this->fail("Expecting an exception, none occurred");
  1960. } catch (moodle_exception $e) {
  1961. $this->assertInstanceOf('dml_exception', $e);
  1962. }
  1963. $record = new stdClass();
  1964. $record->id = 35;
  1965. $record->oneint = 0;
  1966. $record->onenum = 'onestring';
  1967. try {
  1968. $DB->import_record($tablename, $record);
  1969. $this->fail("Expecting an exception, none occurred");
  1970. } catch (moodle_exception $e) {
  1971. $this->assertInstanceOf('dml_exception', $e);
  1972. }
  1973. // Check empty strings are set properly in string types.
  1974. $record = new stdClass();
  1975. $record->id = 44;
  1976. $record->oneint = 0;
  1977. $record->onenum = 0;
  1978. $record->onechar = '';
  1979. $record->onetext = '';
  1980. $this->assertTrue($DB->import_record($tablename, $record));
  1981. $record = $DB->get_record($tablename, array('id' => 44));
  1982. $this->assertTrue($record->onechar === '');
  1983. $this->assertTrue($record->onetext === '');
  1984. // Check operation ((210.10 + 39.92) - 150.02) against numeric types.
  1985. $record = new stdClass();
  1986. $record->id = 47;
  1987. $record->oneint = ((210.10 + 39.92) - 150.02);
  1988. $record->onenum = ((210.10 + 39.92) - 150.02);
  1989. $this->assertTrue($DB->import_record($tablename, $record));
  1990. $record = $DB->get_record($tablename, array('id' => 47));
  1991. $this->assertEquals(100, $record->oneint);
  1992. $this->assertEquals(100, $record->onenum);
  1993. // Check various quotes/backslashes combinations in string types.
  1994. $i = 50;
  1995. $teststrings = array(
  1996. 'backslashes and quotes alone (even): "" \'\' \\\\',
  1997. 'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
  1998. 'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
  1999. 'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
  2000. foreach ($teststrings as $teststring) {
  2001. $record = new stdClass();
  2002. $record->id = $i;
  2003. $record->onechar = $teststring;
  2004. $record->onetext = $teststring;
  2005. $this->assertTrue($DB->import_record($tablename, $record));
  2006. $record = $DB->get_record($tablename, array('id' => $i));
  2007. $this->assertEquals($teststring, $record->onechar);
  2008. $this->assertEquals($teststring, $record->onetext);
  2009. $i = $i + 3;
  2010. }
  2011. // Check LOBs in text/binary columns.
  2012. $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
  2013. $record = new stdClass();
  2014. $record->id = 70;
  2015. $record->onetext = $clob;
  2016. $record->onebinary = '';
  2017. $this->assertTrue($DB->import_record($tablename, $record));
  2018. $rs = $DB->get_recordset($tablename, array('id' => 70));
  2019. $record = $rs->current();
  2020. $rs->close();
  2021. $this->assertEquals($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
  2022. $blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
  2023. $record = new stdClass();
  2024. $record->id = 71;
  2025. $record->onetext = '';
  2026. $record->onebinary = $blob;
  2027. $this->assertTrue($DB->import_record($tablename, $record));
  2028. $rs = $DB->get_recordset($tablename, array('id' => 71));
  2029. $record = $rs->current();
  2030. $rs->close();
  2031. $this->assertEquals($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
  2032. // And "small" LOBs too, just in case.
  2033. $newclob = substr($clob, 0, 500);
  2034. $newblob = substr($blob, 0, 250);
  2035. $record = new stdClass();
  2036. $record->id = 73;
  2037. $record->onetext = $newclob;
  2038. $record->onebinary = $newblob;
  2039. $this->assertTrue($DB->import_record($tablename, $record));
  2040. $rs = $DB->get_recordset($tablename, array('id' => 73));
  2041. $record = $rs->current();
  2042. $rs->close();
  2043. $this->assertEquals($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
  2044. $this->assertEquals($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
  2045. $this->assertEquals(false, $rs->key()); // Ensure recordset key() method to be working ok after closing.
  2046. }
  2047. public function test_update_record_raw() {
  2048. $DB = $this->tdb;
  2049. $dbman = $DB->get_manager();
  2050. $table = $this->get_test_table();
  2051. $tablename = $table->getName();
  2052. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2053. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2054. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2055. $dbman->create_table($table);
  2056. $DB->insert_record($tablename, array('course' => 1));
  2057. $DB->insert_record($tablename, array('course' => 3));
  2058. $record = $DB->get_record($tablename, array('course' => 1));
  2059. $record->course = 2;
  2060. $this->assertTrue($DB->update_record_raw($tablename, $record));
  2061. $this->assertEquals(0, $DB->count_records($tablename, array('course' => 1)));
  2062. $this->assertEquals(1, $DB->count_records($tablename, array('course' => 2)));
  2063. $this->assertEquals(1, $DB->count_records($tablename, array('course' => 3)));
  2064. $record = $DB->get_record($tablename, array('course' => 3));
  2065. $record->xxxxx = 2;
  2066. try {
  2067. $DB->update_record_raw($tablename, $record);
  2068. $this->fail("Expecting an exception, none occurred");
  2069. } catch (moodle_exception $e) {
  2070. $this->assertInstanceOf('moodle_exception', $e);
  2071. }
  2072. $record = $DB->get_record($tablename, array('course' => 3));
  2073. unset($record->id);
  2074. try {
  2075. $DB->update_record_raw($tablename, $record);
  2076. $this->fail("Expecting an exception, none occurred");
  2077. } catch (moodle_exception $e) {
  2078. $this->assertInstanceOf('coding_exception', $e);
  2079. }
  2080. }
  2081. public function test_update_record() {
  2082. // All the information in this test is fetched from DB by get_record() so we
  2083. // have such method properly tested against nulls, empties and friends...
  2084. $DB = $this->tdb;
  2085. $dbman = $DB->get_manager();
  2086. $table = $this->get_test_table();
  2087. $tablename = $table->getName();
  2088. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2089. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2090. $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, null, null, 100);
  2091. $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
  2092. $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
  2093. $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
  2094. $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
  2095. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2096. $dbman->create_table($table);
  2097. $DB->insert_record($tablename, array('course' => 1));
  2098. $record = $DB->get_record($tablename, array('course' => 1));
  2099. $record->course = 2;
  2100. $this->assertTrue($DB->update_record($tablename, $record));
  2101. $this->assertFalse($record = $DB->get_record($tablename, array('course' => 1)));
  2102. $this->assertNotEmpty($record = $DB->get_record($tablename, array('course' => 2)));
  2103. $this->assertEquals(100, $record->oneint); // Just check column defaults have been applied.
  2104. $this->assertEquals(200, $record->onenum);
  2105. $this->assertSame('onestring', $record->onechar);
  2106. $this->assertNull($record->onetext);
  2107. $this->assertNull($record->onebinary);
  2108. // Check nulls are set properly for all types.
  2109. $record->oneint = null;
  2110. $record->onenum = null;
  2111. $record->onechar = null;
  2112. $record->onetext = null;
  2113. $record->onebinary = null;
  2114. $DB->update_record($tablename, $record);
  2115. $record = $DB->get_record($tablename, array('course' => 2));
  2116. $this->assertNull($record->oneint);
  2117. $this->assertNull($record->onenum);
  2118. $this->assertNull($record->onechar);
  2119. $this->assertNull($record->onetext);
  2120. $this->assertNull($record->onebinary);
  2121. // Check zeros are set properly for all types.
  2122. $record->oneint = 0;
  2123. $record->onenum = 0;
  2124. $DB->update_record($tablename, $record);
  2125. $record = $DB->get_record($tablename, array('course' => 2));
  2126. $this->assertEquals(0, $record->oneint);
  2127. $this->assertEquals(0, $record->onenum);
  2128. // Check booleans are set properly for all types.
  2129. $record->oneint = true; // Trues.
  2130. $record->onenum = true;
  2131. $record->onechar = true;
  2132. $record->onetext = true;
  2133. $DB->update_record($tablename, $record);
  2134. $record = $DB->get_record($tablename, array('course' => 2));
  2135. $this->assertEquals(1, $record->oneint);
  2136. $this->assertEquals(1, $record->onenum);
  2137. $this->assertEquals(1, $record->onechar);
  2138. $this->assertEquals(1, $record->onetext);
  2139. $record->oneint = false; // Falses.
  2140. $record->onenum = false;
  2141. $record->onechar = false;
  2142. $record->onetext = false;
  2143. $DB->update_record($tablename, $record);
  2144. $record = $DB->get_record($tablename, array('course' => 2));
  2145. $this->assertEquals(0, $record->oneint);
  2146. $this->assertEquals(0, $record->onenum);
  2147. $this->assertEquals(0, $record->onechar);
  2148. $this->assertEquals(0, $record->onetext);
  2149. // Check string data causes exception in numeric types.
  2150. $record->oneint = 'onestring';
  2151. $record->onenum = 0;
  2152. try {
  2153. $DB->update_record($tablename, $record);
  2154. $this->fail("Expecting an exception, none occurred");
  2155. } catch (moodle_exception $e) {
  2156. $this->assertInstanceOf('dml_exception', $e);
  2157. }
  2158. $record->oneint = 0;
  2159. $record->onenum = 'onestring';
  2160. try {
  2161. $DB->update_record($tablename, $record);
  2162. $this->fail("Expecting an exception, none occurred");
  2163. } catch (moodle_exception $e) {
  2164. $this->assertInstanceOf('dml_exception', $e);
  2165. }
  2166. // Check empty string data is stored as 0 in numeric datatypes.
  2167. $record->oneint = ''; // Empty string.
  2168. $record->onenum = 0;
  2169. $DB->update_record($tablename, $record);
  2170. $record = $DB->get_record($tablename, array('course' => 2));
  2171. $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
  2172. $record->oneint = 0;
  2173. $record->onenum = ''; // Empty string.
  2174. $DB->update_record($tablename, $record);
  2175. $record = $DB->get_record($tablename, array('course' => 2));
  2176. $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
  2177. // Check empty strings are set properly in string types.
  2178. $record->oneint = 0;
  2179. $record->onenum = 0;
  2180. $record->onechar = '';
  2181. $record->onetext = '';
  2182. $DB->update_record($tablename, $record);
  2183. $record = $DB->get_record($tablename, array('course' => 2));
  2184. $this->assertTrue($record->onechar === '');
  2185. $this->assertTrue($record->onetext === '');
  2186. // Check operation ((210.10 + 39.92) - 150.02) against numeric types.
  2187. $record->oneint = ((210.10 + 39.92) - 150.02);
  2188. $record->onenum = ((210.10 + 39.92) - 150.02);
  2189. $DB->update_record($tablename, $record);
  2190. $record = $DB->get_record($tablename, array('course' => 2));
  2191. $this->assertEquals(100, $record->oneint);
  2192. $this->assertEquals(100, $record->onenum);
  2193. // Check various quotes/backslashes combinations in string types.
  2194. $teststrings = array(
  2195. 'backslashes and quotes alone (even): "" \'\' \\\\',
  2196. 'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
  2197. 'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
  2198. 'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
  2199. foreach ($teststrings as $teststring) {
  2200. $record->onechar = $teststring;
  2201. $record->onetext = $teststring;
  2202. $DB->update_record($tablename, $record);
  2203. $record = $DB->get_record($tablename, array('course' => 2));
  2204. $this->assertEquals($teststring, $record->onechar);
  2205. $this->assertEquals($teststring, $record->onetext);
  2206. }
  2207. // Check LOBs in text/binary columns.
  2208. $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
  2209. $blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
  2210. $record->onetext = $clob;
  2211. $record->onebinary = $blob;
  2212. $DB->update_record($tablename, $record);
  2213. $record = $DB->get_record($tablename, array('course' => 2));
  2214. $this->assertEquals($clob, $record->onetext, 'Test CLOB update (full contents output disabled)');
  2215. $this->assertEquals($blob, $record->onebinary, 'Test BLOB update (full contents output disabled)');
  2216. // And "small" LOBs too, just in case.
  2217. $newclob = substr($clob, 0, 500);
  2218. $newblob = substr($blob, 0, 250);
  2219. $record->onetext = $newclob;
  2220. $record->onebinary = $newblob;
  2221. $DB->update_record($tablename, $record);
  2222. $record = $DB->get_record($tablename, array('course' => 2));
  2223. $this->assertEquals($newclob, $record->onetext, 'Test "small" CLOB update (full contents output disabled)');
  2224. $this->assertEquals($newblob, $record->onebinary, 'Test "small" BLOB update (full contents output disabled)');
  2225. // Test saving a float in a CHAR column, and reading it back.
  2226. $id = $DB->insert_record($tablename, array('onechar' => 'X'));
  2227. $DB->update_record($tablename, array('id' => $id, 'onechar' => 1.0));
  2228. $this->assertEquals(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  2229. $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e20));
  2230. $this->assertEquals(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  2231. $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-4));
  2232. $this->assertEquals(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  2233. $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-5));
  2234. $this->assertEquals(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  2235. $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-300));
  2236. $this->assertEquals(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  2237. $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e300));
  2238. $this->assertEquals(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  2239. // Test saving a float in a TEXT column, and reading it back.
  2240. $id = $DB->insert_record($tablename, array('onetext' => 'X'));
  2241. $DB->update_record($tablename, array('id' => $id, 'onetext' => 1.0));
  2242. $this->assertEquals(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  2243. $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e20));
  2244. $this->assertEquals(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  2245. $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-4));
  2246. $this->assertEquals(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  2247. $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-5));
  2248. $this->assertEquals(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  2249. $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-300));
  2250. $this->assertEquals(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  2251. $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e300));
  2252. $this->assertEquals(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  2253. }
  2254. public function test_set_field() {
  2255. $DB = $this->tdb;
  2256. $dbman = $DB->get_manager();
  2257. $table = $this->get_test_table();
  2258. $tablename = $table->getName();
  2259. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2260. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2261. $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
  2262. $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
  2263. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2264. $dbman->create_table($table);
  2265. // Simple set_field.
  2266. $id1 = $DB->insert_record($tablename, array('course' => 1));
  2267. $id2 = $DB->insert_record($tablename, array('course' => 1));
  2268. $id3 = $DB->insert_record($tablename, array('course' => 3));
  2269. $this->assertTrue($DB->set_field($tablename, 'course', 2, array('id' => $id1)));
  2270. $this->assertEquals(2, $DB->get_field($tablename, 'course', array('id' => $id1)));
  2271. $this->assertEquals(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
  2272. $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
  2273. $DB->delete_records($tablename, array());
  2274. // Multiple fields affected.
  2275. $id1 = $DB->insert_record($tablename, array('course' => 1));
  2276. $id2 = $DB->insert_record($tablename, array('course' => 1));
  2277. $id3 = $DB->insert_record($tablename, array('course' => 3));
  2278. $DB->set_field($tablename, 'course', '5', array('course' => 1));
  2279. $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
  2280. $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
  2281. $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
  2282. $DB->delete_records($tablename, array());
  2283. // No field affected.
  2284. $id1 = $DB->insert_record($tablename, array('course' => 1));
  2285. $id2 = $DB->insert_record($tablename, array('course' => 1));
  2286. $id3 = $DB->insert_record($tablename, array('course' => 3));
  2287. $DB->set_field($tablename, 'course', '5', array('course' => 0));
  2288. $this->assertEquals(1, $DB->get_field($tablename, 'course', array('id' => $id1)));
  2289. $this->assertEquals(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
  2290. $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
  2291. $DB->delete_records($tablename, array());
  2292. // All fields - no condition.
  2293. $id1 = $DB->insert_record($tablename, array('course' => 1));
  2294. $id2 = $DB->insert_record($tablename, array('course' => 1));
  2295. $id3 = $DB->insert_record($tablename, array('course' => 3));
  2296. $DB->set_field($tablename, 'course', 5, array());
  2297. $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
  2298. $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
  2299. $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id3)));
  2300. // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
  2301. $conditions = array('onetext' => '1');
  2302. try {
  2303. $DB->set_field($tablename, 'onechar', 'frog', $conditions);
  2304. if (debugging()) {
  2305. // Only in debug mode - hopefully all devs test code in debug mode...
  2306. $this->fail('An Exception is missing, expected due to equating of text fields');
  2307. }
  2308. } catch (moodle_exception $e) {
  2309. $this->assertInstanceOf('dml_exception', $e);
  2310. $this->assertSame('textconditionsnotallowed', $e->errorcode);
  2311. }
  2312. // Test saving a float in a CHAR column, and reading it back.
  2313. $id = $DB->insert_record($tablename, array('onechar' => 'X'));
  2314. $DB->set_field($tablename, 'onechar', 1.0, array('id' => $id));
  2315. $this->assertEquals(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  2316. $DB->set_field($tablename, 'onechar', 1e20, array('id' => $id));
  2317. $this->assertEquals(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  2318. $DB->set_field($tablename, 'onechar', 1e-4, array('id' => $id));
  2319. $this->assertEquals(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  2320. $DB->set_field($tablename, 'onechar', 1e-5, array('id' => $id));
  2321. $this->assertEquals(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  2322. $DB->set_field($tablename, 'onechar', 1e-300, array('id' => $id));
  2323. $this->assertEquals(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  2324. $DB->set_field($tablename, 'onechar', 1e300, array('id' => $id));
  2325. $this->assertEquals(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
  2326. // Test saving a float in a TEXT column, and reading it back.
  2327. $id = $DB->insert_record($tablename, array('onetext' => 'X'));
  2328. $DB->set_field($tablename, 'onetext', 1.0, array('id' => $id));
  2329. $this->assertEquals(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  2330. $DB->set_field($tablename, 'onetext', 1e20, array('id' => $id));
  2331. $this->assertEquals(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  2332. $DB->set_field($tablename, 'onetext', 1e-4, array('id' => $id));
  2333. $this->assertEquals(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  2334. $DB->set_field($tablename, 'onetext', 1e-5, array('id' => $id));
  2335. $this->assertEquals(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  2336. $DB->set_field($tablename, 'onetext', 1e-300, array('id' => $id));
  2337. $this->assertEquals(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  2338. $DB->set_field($tablename, 'onetext', 1e300, array('id' => $id));
  2339. $this->assertEquals(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
  2340. // Note: All the nulls, booleans, empties, quoted and backslashes tests
  2341. // go to set_field_select() because set_field() is just one wrapper over it.
  2342. }
  2343. public function test_set_field_select() {
  2344. // All the information in this test is fetched from DB by get_field() so we
  2345. // have such method properly tested against nulls, empties and friends...
  2346. $DB = $this->tdb;
  2347. $dbman = $DB->get_manager();
  2348. $table = $this->get_test_table();
  2349. $tablename = $table->getName();
  2350. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2351. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2352. $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, null, null);
  2353. $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null);
  2354. $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
  2355. $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
  2356. $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
  2357. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2358. $dbman->create_table($table);
  2359. $DB->insert_record($tablename, array('course' => 1));
  2360. $this->assertTrue($DB->set_field_select($tablename, 'course', 2, 'id = ?', array(1)));
  2361. $this->assertEquals(2, $DB->get_field($tablename, 'course', array('id' => 1)));
  2362. // Check nulls are set properly for all types.
  2363. $DB->set_field_select($tablename, 'oneint', null, 'id = ?', array(1)); // Trues.
  2364. $DB->set_field_select($tablename, 'onenum', null, 'id = ?', array(1));
  2365. $DB->set_field_select($tablename, 'onechar', null, 'id = ?', array(1));
  2366. $DB->set_field_select($tablename, 'onetext', null, 'id = ?', array(1));
  2367. $DB->set_field_select($tablename, 'onebinary', null, 'id = ?', array(1));
  2368. $this->assertNull($DB->get_field($tablename, 'oneint', array('id' => 1)));
  2369. $this->assertNull($DB->get_field($tablename, 'onenum', array('id' => 1)));
  2370. $this->assertNull($DB->get_field($tablename, 'onechar', array('id' => 1)));
  2371. $this->assertNull($DB->get_field($tablename, 'onetext', array('id' => 1)));
  2372. $this->assertNull($DB->get_field($tablename, 'onebinary', array('id' => 1)));
  2373. // Check zeros are set properly for all types.
  2374. $DB->set_field_select($tablename, 'oneint', 0, 'id = ?', array(1));
  2375. $DB->set_field_select($tablename, 'onenum', 0, 'id = ?', array(1));
  2376. $this->assertEquals(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
  2377. $this->assertEquals(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
  2378. // Check booleans are set properly for all types.
  2379. $DB->set_field_select($tablename, 'oneint', true, 'id = ?', array(1)); // Trues.
  2380. $DB->set_field_select($tablename, 'onenum', true, 'id = ?', array(1));
  2381. $DB->set_field_select($tablename, 'onechar', true, 'id = ?', array(1));
  2382. $DB->set_field_select($tablename, 'onetext', true, 'id = ?', array(1));
  2383. $this->assertEquals(1, $DB->get_field($tablename, 'oneint', array('id' => 1)));
  2384. $this->assertEquals(1, $DB->get_field($tablename, 'onenum', array('id' => 1)));
  2385. $this->assertEquals(1, $DB->get_field($tablename, 'onechar', array('id' => 1)));
  2386. $this->assertEquals(1, $DB->get_field($tablename, 'onetext', array('id' => 1)));
  2387. $DB->set_field_select($tablename, 'oneint', false, 'id = ?', array(1)); // Falses.
  2388. $DB->set_field_select($tablename, 'onenum', false, 'id = ?', array(1));
  2389. $DB->set_field_select($tablename, 'onechar', false, 'id = ?', array(1));
  2390. $DB->set_field_select($tablename, 'onetext', false, 'id = ?', array(1));
  2391. $this->assertEquals(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
  2392. $this->assertEquals(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
  2393. $this->assertEquals(0, $DB->get_field($tablename, 'onechar', array('id' => 1)));
  2394. $this->assertEquals(0, $DB->get_field($tablename, 'onetext', array('id' => 1)));
  2395. // Check string data causes exception in numeric types.
  2396. try {
  2397. $DB->set_field_select($tablename, 'oneint', 'onestring', 'id = ?', array(1));
  2398. $this->fail("Expecting an exception, none occurred");
  2399. } catch (moodle_exception $e) {
  2400. $this->assertInstanceOf('dml_exception', $e);
  2401. }
  2402. try {
  2403. $DB->set_field_select($tablename, 'onenum', 'onestring', 'id = ?', array(1));
  2404. $this->fail("Expecting an exception, none occurred");
  2405. } catch (moodle_exception $e) {
  2406. $this->assertInstanceOf('dml_exception', $e);
  2407. }
  2408. // Check empty string data is stored as 0 in numeric datatypes.
  2409. $DB->set_field_select($tablename, 'oneint', '', 'id = ?', array(1));
  2410. $field = $DB->get_field($tablename, 'oneint', array('id' => 1));
  2411. $this->assertTrue(is_numeric($field) && $field == 0);
  2412. $DB->set_field_select($tablename, 'onenum', '', 'id = ?', array(1));
  2413. $field = $DB->get_field($tablename, 'onenum', array('id' => 1));
  2414. $this->assertTrue(is_numeric($field) && $field == 0);
  2415. // Check empty strings are set properly in string types.
  2416. $DB->set_field_select($tablename, 'onechar', '', 'id = ?', array(1));
  2417. $DB->set_field_select($tablename, 'onetext', '', 'id = ?', array(1));
  2418. $this->assertTrue($DB->get_field($tablename, 'onechar', array('id' => 1)) === '');
  2419. $this->assertTrue($DB->get_field($tablename, 'onetext', array('id' => 1)) === '');
  2420. // Check operation ((210.10 + 39.92) - 150.02) against numeric types.
  2421. $DB->set_field_select($tablename, 'oneint', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
  2422. $DB->set_field_select($tablename, 'onenum', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
  2423. $this->assertEquals(100, $DB->get_field($tablename, 'oneint', array('id' => 1)));
  2424. $this->assertEquals(100, $DB->get_field($tablename, 'onenum', array('id' => 1)));
  2425. // Check various quotes/backslashes combinations in string types.
  2426. $teststrings = array(
  2427. 'backslashes and quotes alone (even): "" \'\' \\\\',
  2428. 'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
  2429. 'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
  2430. 'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
  2431. foreach ($teststrings as $teststring) {
  2432. $DB->set_field_select($tablename, 'onechar', $teststring, 'id = ?', array(1));
  2433. $DB->set_field_select($tablename, 'onetext', $teststring, 'id = ?', array(1));
  2434. $this->assertEquals($teststring, $DB->get_field($tablename, 'onechar', array('id' => 1)));
  2435. $this->assertEquals($teststring, $DB->get_field($tablename, 'onetext', array('id' => 1)));
  2436. }
  2437. // Check LOBs in text/binary columns.
  2438. $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
  2439. $blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
  2440. $DB->set_field_select($tablename, 'onetext', $clob, 'id = ?', array(1));
  2441. $DB->set_field_select($tablename, 'onebinary', $blob, 'id = ?', array(1));
  2442. $this->assertEquals($clob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test CLOB set_field (full contents output disabled)');
  2443. $this->assertEquals($blob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test BLOB set_field (full contents output disabled)');
  2444. // And "small" LOBs too, just in case.
  2445. $newclob = substr($clob, 0, 500);
  2446. $newblob = substr($blob, 0, 250);
  2447. $DB->set_field_select($tablename, 'onetext', $newclob, 'id = ?', array(1));
  2448. $DB->set_field_select($tablename, 'onebinary', $newblob, 'id = ?', array(1));
  2449. $this->assertEquals($newclob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test "small" CLOB set_field (full contents output disabled)');
  2450. $this->assertEquals($newblob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test "small" BLOB set_field (full contents output disabled)');
  2451. // This is the failure from MDL-24863. This was giving an error on MSSQL,
  2452. // which converts the '1' to an integer, which cannot then be compared with
  2453. // onetext cast to a varchar. This should be fixed and working now.
  2454. $newchar = 'frog';
  2455. // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
  2456. $params = array('onetext' => '1');
  2457. try {
  2458. $DB->set_field_select($tablename, 'onechar', $newchar, $DB->sql_compare_text('onetext') . ' = ?', $params);
  2459. $this->assertTrue(true, 'No exceptions thrown with numerical text param comparison for text field.');
  2460. } catch (dml_exception $e) {
  2461. $this->assertFalse(true, 'We have an unexpected exception.');
  2462. throw $e;
  2463. }
  2464. }
  2465. public function test_count_records() {
  2466. $DB = $this->tdb;
  2467. $dbman = $DB->get_manager();
  2468. $table = $this->get_test_table();
  2469. $tablename = $table->getName();
  2470. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2471. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2472. $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
  2473. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2474. $dbman->create_table($table);
  2475. $this->assertSame(0, $DB->count_records($tablename));
  2476. $DB->insert_record($tablename, array('course' => 3));
  2477. $DB->insert_record($tablename, array('course' => 4));
  2478. $DB->insert_record($tablename, array('course' => 5));
  2479. $this->assertSame(3, $DB->count_records($tablename));
  2480. // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
  2481. $conditions = array('onetext' => '1');
  2482. try {
  2483. $DB->count_records($tablename, $conditions);
  2484. if (debugging()) {
  2485. // Only in debug mode - hopefully all devs test code in debug mode...
  2486. $this->fail('An Exception is missing, expected due to equating of text fields');
  2487. }
  2488. } catch (moodle_exception $e) {
  2489. $this->assertInstanceOf('dml_exception', $e);
  2490. $this->assertSame('textconditionsnotallowed', $e->errorcode);
  2491. }
  2492. }
  2493. public function test_count_records_select() {
  2494. $DB = $this->tdb;
  2495. $dbman = $DB->get_manager();
  2496. $table = $this->get_test_table();
  2497. $tablename = $table->getName();
  2498. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2499. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2500. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2501. $dbman->create_table($table);
  2502. $this->assertSame(0, $DB->count_records($tablename));
  2503. $DB->insert_record($tablename, array('course' => 3));
  2504. $DB->insert_record($tablename, array('course' => 4));
  2505. $DB->insert_record($tablename, array('course' => 5));
  2506. $this->assertSame(2, $DB->count_records_select($tablename, 'course > ?', array(3)));
  2507. }
  2508. public function test_count_records_sql() {
  2509. $DB = $this->tdb;
  2510. $dbman = $DB->get_manager();
  2511. $table = $this->get_test_table();
  2512. $tablename = $table->getName();
  2513. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2514. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2515. $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
  2516. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2517. $dbman->create_table($table);
  2518. $this->assertSame(0, $DB->count_records($tablename));
  2519. $DB->insert_record($tablename, array('course' => 3, 'onechar' => 'a'));
  2520. $DB->insert_record($tablename, array('course' => 4, 'onechar' => 'b'));
  2521. $DB->insert_record($tablename, array('course' => 5, 'onechar' => 'c'));
  2522. $this->assertSame(2, $DB->count_records_sql("SELECT COUNT(*) FROM {{$tablename}} WHERE course > ?", array(3)));
  2523. // Test invalid use.
  2524. try {
  2525. $DB->count_records_sql("SELECT onechar FROM {{$tablename}} WHERE course = ?", array(3));
  2526. $this->fail('Exception expected when non-number field used in count_records_sql');
  2527. } catch (moodle_exception $e) {
  2528. $this->assertInstanceOf('coding_exception', $e);
  2529. }
  2530. try {
  2531. $DB->count_records_sql("SELECT course FROM {{$tablename}} WHERE 1 = 2");
  2532. $this->fail('Exception expected when non-number field used in count_records_sql');
  2533. } catch (moodle_exception $e) {
  2534. $this->assertInstanceOf('coding_exception', $e);
  2535. }
  2536. }
  2537. public function test_record_exists() {
  2538. $DB = $this->tdb;
  2539. $dbman = $DB->get_manager();
  2540. $table = $this->get_test_table();
  2541. $tablename = $table->getName();
  2542. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2543. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2544. $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
  2545. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2546. $dbman->create_table($table);
  2547. $this->assertEquals(0, $DB->count_records($tablename));
  2548. $this->assertFalse($DB->record_exists($tablename, array('course' => 3)));
  2549. $DB->insert_record($tablename, array('course' => 3));
  2550. $this->assertTrue($DB->record_exists($tablename, array('course' => 3)));
  2551. // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
  2552. $conditions = array('onetext' => '1');
  2553. try {
  2554. $DB->record_exists($tablename, $conditions);
  2555. if (debugging()) {
  2556. // Only in debug mode - hopefully all devs test code in debug mode...
  2557. $this->fail('An Exception is missing, expected due to equating of text fields');
  2558. }
  2559. } catch (moodle_exception $e) {
  2560. $this->assertInstanceOf('dml_exception', $e);
  2561. $this->assertSame('textconditionsnotallowed', $e->errorcode);
  2562. }
  2563. }
  2564. public function test_record_exists_select() {
  2565. $DB = $this->tdb;
  2566. $dbman = $DB->get_manager();
  2567. $table = $this->get_test_table();
  2568. $tablename = $table->getName();
  2569. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2570. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2571. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2572. $dbman->create_table($table);
  2573. $this->assertEquals(0, $DB->count_records($tablename));
  2574. $this->assertFalse($DB->record_exists_select($tablename, "course = ?", array(3)));
  2575. $DB->insert_record($tablename, array('course' => 3));
  2576. $this->assertTrue($DB->record_exists_select($tablename, "course = ?", array(3)));
  2577. }
  2578. public function test_record_exists_sql() {
  2579. $DB = $this->tdb;
  2580. $dbman = $DB->get_manager();
  2581. $table = $this->get_test_table();
  2582. $tablename = $table->getName();
  2583. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2584. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2585. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2586. $dbman->create_table($table);
  2587. $this->assertEquals(0, $DB->count_records($tablename));
  2588. $this->assertFalse($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
  2589. $DB->insert_record($tablename, array('course' => 3));
  2590. $this->assertTrue($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
  2591. }
  2592. public function test_recordset_locks_delete() {
  2593. $DB = $this->tdb;
  2594. $dbman = $DB->get_manager();
  2595. // Setup.
  2596. $table = $this->get_test_table();
  2597. $tablename = $table->getName();
  2598. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2599. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2600. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2601. $dbman->create_table($table);
  2602. $DB->insert_record($tablename, array('course' => 1));
  2603. $DB->insert_record($tablename, array('course' => 2));
  2604. $DB->insert_record($tablename, array('course' => 3));
  2605. $DB->insert_record($tablename, array('course' => 4));
  2606. $DB->insert_record($tablename, array('course' => 5));
  2607. $DB->insert_record($tablename, array('course' => 6));
  2608. // Test against db write locking while on an open recordset.
  2609. $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // Get courses = {3,4}.
  2610. foreach ($rs as $record) {
  2611. $cid = $record->course;
  2612. $DB->delete_records($tablename, array('course' => $cid));
  2613. $this->assertFalse($DB->record_exists($tablename, array('course' => $cid)));
  2614. }
  2615. $rs->close();
  2616. $this->assertEquals(4, $DB->count_records($tablename, array()));
  2617. }
  2618. public function test_recordset_locks_update() {
  2619. $DB = $this->tdb;
  2620. $dbman = $DB->get_manager();
  2621. // Setup.
  2622. $table = $this->get_test_table();
  2623. $tablename = $table->getName();
  2624. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2625. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2626. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2627. $dbman->create_table($table);
  2628. $DB->insert_record($tablename, array('course' => 1));
  2629. $DB->insert_record($tablename, array('course' => 2));
  2630. $DB->insert_record($tablename, array('course' => 3));
  2631. $DB->insert_record($tablename, array('course' => 4));
  2632. $DB->insert_record($tablename, array('course' => 5));
  2633. $DB->insert_record($tablename, array('course' => 6));
  2634. // Test against db write locking while on an open recordset.
  2635. $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // Get courses = {3,4}.
  2636. foreach ($rs as $record) {
  2637. $cid = $record->course;
  2638. $DB->set_field($tablename, 'course', 10, array('course' => $cid));
  2639. $this->assertFalse($DB->record_exists($tablename, array('course' => $cid)));
  2640. }
  2641. $rs->close();
  2642. $this->assertEquals(2, $DB->count_records($tablename, array('course' => 10)));
  2643. }
  2644. public function test_delete_records() {
  2645. $DB = $this->tdb;
  2646. $dbman = $DB->get_manager();
  2647. $table = $this->get_test_table();
  2648. $tablename = $table->getName();
  2649. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2650. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2651. $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
  2652. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2653. $dbman->create_table($table);
  2654. $DB->insert_record($tablename, array('course' => 3));
  2655. $DB->insert_record($tablename, array('course' => 2));
  2656. $DB->insert_record($tablename, array('course' => 2));
  2657. // Delete all records.
  2658. $this->assertTrue($DB->delete_records($tablename));
  2659. $this->assertEquals(0, $DB->count_records($tablename));
  2660. // Delete subset of records.
  2661. $DB->insert_record($tablename, array('course' => 3));
  2662. $DB->insert_record($tablename, array('course' => 2));
  2663. $DB->insert_record($tablename, array('course' => 2));
  2664. $this->assertTrue($DB->delete_records($tablename, array('course' => 2)));
  2665. $this->assertEquals(1, $DB->count_records($tablename));
  2666. // Delete all.
  2667. $this->assertTrue($DB->delete_records($tablename, array()));
  2668. $this->assertEquals(0, $DB->count_records($tablename));
  2669. // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
  2670. $conditions = array('onetext'=>'1');
  2671. try {
  2672. $DB->delete_records($tablename, $conditions);
  2673. if (debugging()) {
  2674. // Only in debug mode - hopefully all devs test code in debug mode...
  2675. $this->fail('An Exception is missing, expected due to equating of text fields');
  2676. }
  2677. } catch (moodle_exception $e) {
  2678. $this->assertInstanceOf('dml_exception', $e);
  2679. $this->assertSame('textconditionsnotallowed', $e->errorcode);
  2680. }
  2681. // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
  2682. $conditions = array('onetext' => 1);
  2683. try {
  2684. $DB->delete_records($tablename, $conditions);
  2685. if (debugging()) {
  2686. // Only in debug mode - hopefully all devs test code in debug mode...
  2687. $this->fail('An Exception is missing, expected due to equating of text fields');
  2688. }
  2689. } catch (moodle_exception $e) {
  2690. $this->assertInstanceOf('dml_exception', $e);
  2691. $this->assertSame('textconditionsnotallowed', $e->errorcode);
  2692. }
  2693. }
  2694. public function test_delete_records_select() {
  2695. $DB = $this->tdb;
  2696. $dbman = $DB->get_manager();
  2697. $table = $this->get_test_table();
  2698. $tablename = $table->getName();
  2699. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2700. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2701. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2702. $dbman->create_table($table);
  2703. $DB->insert_record($tablename, array('course' => 3));
  2704. $DB->insert_record($tablename, array('course' => 2));
  2705. $DB->insert_record($tablename, array('course' => 2));
  2706. $this->assertTrue($DB->delete_records_select($tablename, 'course = ?', array(2)));
  2707. $this->assertEquals(1, $DB->count_records($tablename));
  2708. }
  2709. public function test_delete_records_list() {
  2710. $DB = $this->tdb;
  2711. $dbman = $DB->get_manager();
  2712. $table = $this->get_test_table();
  2713. $tablename = $table->getName();
  2714. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2715. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2716. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2717. $dbman->create_table($table);
  2718. $DB->insert_record($tablename, array('course' => 1));
  2719. $DB->insert_record($tablename, array('course' => 2));
  2720. $DB->insert_record($tablename, array('course' => 3));
  2721. $this->assertTrue($DB->delete_records_list($tablename, 'course', array(2, 3)));
  2722. $this->assertEquals(1, $DB->count_records($tablename));
  2723. $this->assertTrue($DB->delete_records_list($tablename, 'course', array())); // Must delete 0 rows without conditions. MDL-17645.
  2724. $this->assertEquals(1, $DB->count_records($tablename));
  2725. }
  2726. public function test_object_params() {
  2727. $DB = $this->tdb;
  2728. $dbman = $DB->get_manager();
  2729. $table = $this->get_test_table();
  2730. $tablename = $table->getName();
  2731. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2732. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2733. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2734. $dbman->create_table($table);
  2735. $o = new stdClass(); // Objects without __toString - never worked.
  2736. try {
  2737. $DB->fix_sql_params("SELECT {{$tablename}} WHERE course = ? ", array($o));
  2738. $this->fail('coding_exception expected');
  2739. } catch (moodle_exception $e) {
  2740. $this->assertInstanceOf('coding_exception', $e);
  2741. }
  2742. // Objects with __toString() forbidden everywhere since 2.3.
  2743. $o = new dml_test_object_one();
  2744. try {
  2745. $DB->fix_sql_params("SELECT {{$tablename}} WHERE course = ? ", array($o));
  2746. $this->fail('coding_exception expected');
  2747. } catch (moodle_exception $e) {
  2748. $this->assertInstanceOf('coding_exception', $e);
  2749. }
  2750. try {
  2751. $DB->execute("SELECT {{$tablename}} WHERE course = ? ", array($o));
  2752. $this->fail('coding_exception expected');
  2753. } catch (moodle_exception $e) {
  2754. $this->assertInstanceOf('coding_exception', $e);
  2755. }
  2756. try {
  2757. $DB->get_recordset_sql("SELECT {{$tablename}} WHERE course = ? ", array($o));
  2758. $this->fail('coding_exception expected');
  2759. } catch (moodle_exception $e) {
  2760. $this->assertInstanceOf('coding_exception', $e);
  2761. }
  2762. try {
  2763. $DB->get_records_sql("SELECT {{$tablename}} WHERE course = ? ", array($o));
  2764. $this->fail('coding_exception expected');
  2765. } catch (moodle_exception $e) {
  2766. $this->assertInstanceOf('coding_exception', $e);
  2767. }
  2768. try {
  2769. $record = new stdClass();
  2770. $record->course = $o;
  2771. $DB->insert_record_raw($tablename, $record);
  2772. $this->fail('coding_exception expected');
  2773. } catch (moodle_exception $e) {
  2774. $this->assertInstanceOf('coding_exception', $e);
  2775. }
  2776. try {
  2777. $record = new stdClass();
  2778. $record->course = $o;
  2779. $DB->insert_record($tablename, $record);
  2780. $this->fail('coding_exception expected');
  2781. } catch (moodle_exception $e) {
  2782. $this->assertInstanceOf('coding_exception', $e);
  2783. }
  2784. try {
  2785. $record = new stdClass();
  2786. $record->course = $o;
  2787. $DB->import_record($tablename, $record);
  2788. $this->fail('coding_exception expected');
  2789. } catch (moodle_exception $e) {
  2790. $this->assertInstanceOf('coding_exception', $e);
  2791. }
  2792. try {
  2793. $record = new stdClass();
  2794. $record->id = 1;
  2795. $record->course = $o;
  2796. $DB->update_record_raw($tablename, $record);
  2797. $this->fail('coding_exception expected');
  2798. } catch (moodle_exception $e) {
  2799. $this->assertInstanceOf('coding_exception', $e);
  2800. }
  2801. try {
  2802. $record = new stdClass();
  2803. $record->id = 1;
  2804. $record->course = $o;
  2805. $DB->update_record($tablename, $record);
  2806. $this->fail('coding_exception expected');
  2807. } catch (moodle_exception $e) {
  2808. $this->assertInstanceOf('coding_exception', $e);
  2809. }
  2810. try {
  2811. $DB->set_field_select($tablename, 'course', 1, "course = ? ", array($o));
  2812. $this->fail('coding_exception expected');
  2813. } catch (moodle_exception $e) {
  2814. $this->assertInstanceOf('coding_exception', $e);
  2815. }
  2816. try {
  2817. $DB->delete_records_select($tablename, "course = ? ", array($o));
  2818. $this->fail('coding_exception expected');
  2819. } catch (moodle_exception $e) {
  2820. $this->assertInstanceOf('coding_exception', $e);
  2821. }
  2822. }
  2823. public function test_sql_null_from_clause() {
  2824. $DB = $this->tdb;
  2825. $sql = "SELECT 1 AS id ".$DB->sql_null_from_clause();
  2826. $this->assertEquals(1, $DB->get_field_sql($sql));
  2827. }
  2828. public function test_sql_bitand() {
  2829. $DB = $this->tdb;
  2830. $dbman = $DB->get_manager();
  2831. $table = $this->get_test_table();
  2832. $tablename = $table->getName();
  2833. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2834. $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2835. $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2836. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2837. $dbman->create_table($table);
  2838. $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
  2839. $sql = "SELECT ".$DB->sql_bitand(10, 3)." AS res ".$DB->sql_null_from_clause();
  2840. $this->assertEquals(2, $DB->get_field_sql($sql));
  2841. $sql = "SELECT id, ".$DB->sql_bitand('col1', 'col2')." AS res FROM {{$tablename}}";
  2842. $result = $DB->get_records_sql($sql);
  2843. $this->assertCount(1, $result);
  2844. $this->assertEquals(2, reset($result)->res);
  2845. $sql = "SELECT id, ".$DB->sql_bitand('col1', '?')." AS res FROM {{$tablename}}";
  2846. $result = $DB->get_records_sql($sql, array(10));
  2847. $this->assertCount(1, $result);
  2848. $this->assertEquals(2, reset($result)->res);
  2849. }
  2850. public function test_sql_bitnot() {
  2851. $DB = $this->tdb;
  2852. $not = $DB->sql_bitnot(2);
  2853. $notlimited = $DB->sql_bitand($not, 7); // Might be positive or negative number which can not fit into PHP INT!
  2854. $sql = "SELECT $notlimited AS res ".$DB->sql_null_from_clause();
  2855. $this->assertEquals(5, $DB->get_field_sql($sql));
  2856. }
  2857. public function test_sql_bitor() {
  2858. $DB = $this->tdb;
  2859. $dbman = $DB->get_manager();
  2860. $table = $this->get_test_table();
  2861. $tablename = $table->getName();
  2862. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2863. $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2864. $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2865. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2866. $dbman->create_table($table);
  2867. $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
  2868. $sql = "SELECT ".$DB->sql_bitor(10, 3)." AS res ".$DB->sql_null_from_clause();
  2869. $this->assertEquals(11, $DB->get_field_sql($sql));
  2870. $sql = "SELECT id, ".$DB->sql_bitor('col1', 'col2')." AS res FROM {{$tablename}}";
  2871. $result = $DB->get_records_sql($sql);
  2872. $this->assertCount(1, $result);
  2873. $this->assertEquals(11, reset($result)->res);
  2874. $sql = "SELECT id, ".$DB->sql_bitor('col1', '?')." AS res FROM {{$tablename}}";
  2875. $result = $DB->get_records_sql($sql, array(10));
  2876. $this->assertCount(1, $result);
  2877. $this->assertEquals(11, reset($result)->res);
  2878. }
  2879. public function test_sql_bitxor() {
  2880. $DB = $this->tdb;
  2881. $dbman = $DB->get_manager();
  2882. $table = $this->get_test_table();
  2883. $tablename = $table->getName();
  2884. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2885. $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2886. $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2887. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2888. $dbman->create_table($table);
  2889. $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
  2890. $sql = "SELECT ".$DB->sql_bitxor(10, 3)." AS res ".$DB->sql_null_from_clause();
  2891. $this->assertEquals(9, $DB->get_field_sql($sql));
  2892. $sql = "SELECT id, ".$DB->sql_bitxor('col1', 'col2')." AS res FROM {{$tablename}}";
  2893. $result = $DB->get_records_sql($sql);
  2894. $this->assertCount(1, $result);
  2895. $this->assertEquals(9, reset($result)->res);
  2896. $sql = "SELECT id, ".$DB->sql_bitxor('col1', '?')." AS res FROM {{$tablename}}";
  2897. $result = $DB->get_records_sql($sql, array(10));
  2898. $this->assertCount(1, $result);
  2899. $this->assertEquals(9, reset($result)->res);
  2900. }
  2901. public function test_sql_modulo() {
  2902. $DB = $this->tdb;
  2903. $sql = "SELECT ".$DB->sql_modulo(10, 7)." AS res ".$DB->sql_null_from_clause();
  2904. $this->assertEquals(3, $DB->get_field_sql($sql));
  2905. }
  2906. public function test_sql_ceil() {
  2907. $DB = $this->tdb;
  2908. $sql = "SELECT ".$DB->sql_ceil(665.666)." AS res ".$DB->sql_null_from_clause();
  2909. $this->assertEquals(666, $DB->get_field_sql($sql));
  2910. }
  2911. public function test_cast_char2int() {
  2912. $DB = $this->tdb;
  2913. $dbman = $DB->get_manager();
  2914. $table1 = $this->get_test_table("1");
  2915. $tablename1 = $table1->getName();
  2916. $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2917. $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
  2918. $table1->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
  2919. $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2920. $dbman->create_table($table1);
  2921. $DB->insert_record($tablename1, array('name'=>'0100', 'nametext'=>'0200'));
  2922. $DB->insert_record($tablename1, array('name'=>'10', 'nametext'=>'20'));
  2923. $table2 = $this->get_test_table("2");
  2924. $tablename2 = $table2->getName();
  2925. $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2926. $table2->add_field('res', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2927. $table2->add_field('restext', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  2928. $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2929. $dbman->create_table($table2);
  2930. $DB->insert_record($tablename2, array('res'=>100, 'restext'=>200));
  2931. // Casting varchar field.
  2932. $sql = "SELECT *
  2933. FROM {".$tablename1."} t1
  2934. JOIN {".$tablename2."} t2 ON ".$DB->sql_cast_char2int("t1.name")." = t2.res ";
  2935. $records = $DB->get_records_sql($sql);
  2936. $this->assertCount(1, $records);
  2937. // Also test them in order clauses.
  2938. $sql = "SELECT * FROM {{$tablename1}} ORDER BY ".$DB->sql_cast_char2int('name');
  2939. $records = $DB->get_records_sql($sql);
  2940. $this->assertCount(2, $records);
  2941. $this->assertSame('10', reset($records)->name);
  2942. $this->assertSame('0100', next($records)->name);
  2943. // Casting text field.
  2944. $sql = "SELECT *
  2945. FROM {".$tablename1."} t1
  2946. JOIN {".$tablename2."} t2 ON ".$DB->sql_cast_char2int("t1.nametext", true)." = t2.restext ";
  2947. $records = $DB->get_records_sql($sql);
  2948. $this->assertCount(1, $records);
  2949. // Also test them in order clauses.
  2950. $sql = "SELECT * FROM {{$tablename1}} ORDER BY ".$DB->sql_cast_char2int('nametext', true);
  2951. $records = $DB->get_records_sql($sql);
  2952. $this->assertCount(2, $records);
  2953. $this->assertSame('20', reset($records)->nametext);
  2954. $this->assertSame('0200', next($records)->nametext);
  2955. }
  2956. public function test_cast_char2real() {
  2957. $DB = $this->tdb;
  2958. $dbman = $DB->get_manager();
  2959. $table = $this->get_test_table();
  2960. $tablename = $table->getName();
  2961. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2962. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
  2963. $table->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
  2964. $table->add_field('res', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null);
  2965. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  2966. $dbman->create_table($table);
  2967. $DB->insert_record($tablename, array('name'=>'10.10', 'nametext'=>'10.10', 'res'=>5.1));
  2968. $DB->insert_record($tablename, array('name'=>'91.10', 'nametext'=>'91.10', 'res'=>666));
  2969. $DB->insert_record($tablename, array('name'=>'011.10', 'nametext'=>'011.10', 'res'=>10.1));
  2970. // Casting varchar field.
  2971. $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_cast_char2real('name')." > res";
  2972. $records = $DB->get_records_sql($sql);
  2973. $this->assertCount(2, $records);
  2974. // Also test them in order clauses.
  2975. $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_cast_char2real('name');
  2976. $records = $DB->get_records_sql($sql);
  2977. $this->assertCount(3, $records);
  2978. $this->assertSame('10.10', reset($records)->name);
  2979. $this->assertSame('011.10', next($records)->name);
  2980. $this->assertSame('91.10', next($records)->name);
  2981. // Casting text field.
  2982. $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_cast_char2real('nametext', true)." > res";
  2983. $records = $DB->get_records_sql($sql);
  2984. $this->assertCount(2, $records);
  2985. // Also test them in order clauses.
  2986. $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_cast_char2real('nametext', true);
  2987. $records = $DB->get_records_sql($sql);
  2988. $this->assertCount(3, $records);
  2989. $this->assertSame('10.10', reset($records)->nametext);
  2990. $this->assertSame('011.10', next($records)->nametext);
  2991. $this->assertSame('91.10', next($records)->nametext);
  2992. }
  2993. public function test_sql_compare_text() {
  2994. $DB = $this->tdb;
  2995. $dbman = $DB->get_manager();
  2996. $table = $this->get_test_table();
  2997. $tablename = $table->getName();
  2998. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  2999. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
  3000. $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
  3001. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3002. $dbman->create_table($table);
  3003. $DB->insert_record($tablename, array('name'=>'abcd', 'description'=>'abcd'));
  3004. $DB->insert_record($tablename, array('name'=>'abcdef', 'description'=>'bbcdef'));
  3005. $DB->insert_record($tablename, array('name'=>'aaaa', 'description'=>'aaaacccccccccccccccccc'));
  3006. $DB->insert_record($tablename, array('name'=>'xxxx', 'description'=>'123456789a123456789b123456789c123456789d'));
  3007. // Only some supported databases truncate TEXT fields for comparisons, currently MSSQL and Oracle.
  3008. $dbtruncatestextfields = ($DB->get_dbfamily() == 'mssql' || $DB->get_dbfamily() == 'oracle');
  3009. if ($dbtruncatestextfields) {
  3010. // Ensure truncation behaves as expected.
  3011. $sql = "SELECT " . $DB->sql_compare_text('description') . " AS field FROM {{$tablename}} WHERE name = ?";
  3012. $description = $DB->get_field_sql($sql, array('xxxx'));
  3013. // Should truncate to 32 chars (the default).
  3014. $this->assertEquals('123456789a123456789b123456789c12', $description);
  3015. $sql = "SELECT " . $DB->sql_compare_text('description', 35) . " AS field FROM {{$tablename}} WHERE name = ?";
  3016. $description = $DB->get_field_sql($sql, array('xxxx'));
  3017. // Should truncate to the specified number of chars.
  3018. $this->assertEquals('123456789a123456789b123456789c12345', $description);
  3019. }
  3020. // Ensure text field comparison is successful.
  3021. $sql = "SELECT * FROM {{$tablename}} WHERE name = ".$DB->sql_compare_text('description');
  3022. $records = $DB->get_records_sql($sql);
  3023. $this->assertCount(1, $records);
  3024. $sql = "SELECT * FROM {{$tablename}} WHERE name = ".$DB->sql_compare_text('description', 4);
  3025. $records = $DB->get_records_sql($sql);
  3026. if ($dbtruncatestextfields) {
  3027. // Should truncate description to 4 characters before comparing.
  3028. $this->assertCount(2, $records);
  3029. } else {
  3030. // Should leave untruncated, so one less match.
  3031. $this->assertCount(1, $records);
  3032. }
  3033. }
  3034. public function test_unique_index_collation_trouble() {
  3035. // Note: this is a work in progress, we should probably move this to ddl test.
  3036. $DB = $this->tdb;
  3037. $dbman = $DB->get_manager();
  3038. $table = $this->get_test_table();
  3039. $tablename = $table->getName();
  3040. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3041. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
  3042. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3043. $table->add_index('name', XMLDB_INDEX_UNIQUE, array('name'));
  3044. $dbman->create_table($table);
  3045. $DB->insert_record($tablename, array('name'=>'aaa'));
  3046. try {
  3047. $DB->insert_record($tablename, array('name'=>'AAA'));
  3048. } catch (moodle_exception $e) {
  3049. // TODO: ignore case insensitive uniqueness problems for now.
  3050. // $this->fail("Unique index is case sensitive - this may cause problems in some tables");
  3051. }
  3052. try {
  3053. $DB->insert_record($tablename, array('name'=>'aäa'));
  3054. $DB->insert_record($tablename, array('name'=>'aáa'));
  3055. $this->assertTrue(true);
  3056. } catch (moodle_exception $e) {
  3057. $family = $DB->get_dbfamily();
  3058. if ($family === 'mysql' or $family === 'mssql') {
  3059. $this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages. This is usually caused by accent insensitive default collation.");
  3060. } else {
  3061. // This should not happen, PostgreSQL and Oracle do not support accent insensitive uniqueness.
  3062. $this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages.");
  3063. }
  3064. throw($e);
  3065. }
  3066. }
  3067. public function test_sql_binary_equal() {
  3068. $DB = $this->tdb;
  3069. $dbman = $DB->get_manager();
  3070. $table = $this->get_test_table();
  3071. $tablename = $table->getName();
  3072. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3073. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
  3074. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3075. $dbman->create_table($table);
  3076. $DB->insert_record($tablename, array('name'=>'aaa'));
  3077. $DB->insert_record($tablename, array('name'=>'aáa'));
  3078. $DB->insert_record($tablename, array('name'=>'aäa'));
  3079. $DB->insert_record($tablename, array('name'=>'bbb'));
  3080. $DB->insert_record($tablename, array('name'=>'BBB'));
  3081. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array('bbb'));
  3082. $this->assertEquals(1, count($records), 'SQL operator "=" is expected to be case sensitive');
  3083. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array('aaa'));
  3084. $this->assertEquals(1, count($records), 'SQL operator "=" is expected to be accent sensitive');
  3085. }
  3086. public function test_sql_like() {
  3087. $DB = $this->tdb;
  3088. $dbman = $DB->get_manager();
  3089. $table = $this->get_test_table();
  3090. $tablename = $table->getName();
  3091. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3092. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
  3093. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3094. $dbman->create_table($table);
  3095. $DB->insert_record($tablename, array('name'=>'SuperDuperRecord'));
  3096. $DB->insert_record($tablename, array('name'=>'Nodupor'));
  3097. $DB->insert_record($tablename, array('name'=>'ouch'));
  3098. $DB->insert_record($tablename, array('name'=>'ouc_'));
  3099. $DB->insert_record($tablename, array('name'=>'ouc%'));
  3100. $DB->insert_record($tablename, array('name'=>'aui'));
  3101. $DB->insert_record($tablename, array('name'=>'aüi'));
  3102. $DB->insert_record($tablename, array('name'=>'aÜi'));
  3103. $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false);
  3104. $records = $DB->get_records_sql($sql, array("%dup_r%"));
  3105. $this->assertCount(2, $records);
  3106. $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true);
  3107. $records = $DB->get_records_sql($sql, array("%dup%"));
  3108. $this->assertCount(1, $records);
  3109. $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?'); // Defaults.
  3110. $records = $DB->get_records_sql($sql, array("%dup%"));
  3111. $this->assertCount(1, $records);
  3112. $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true);
  3113. $records = $DB->get_records_sql($sql, array("ouc\\_"));
  3114. $this->assertCount(1, $records);
  3115. $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, false, '|');
  3116. $records = $DB->get_records_sql($sql, array($DB->sql_like_escape("ouc%", '|')));
  3117. $this->assertCount(1, $records);
  3118. $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true);
  3119. $records = $DB->get_records_sql($sql, array('aui'));
  3120. $this->assertCount(1, $records);
  3121. $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, true); // NOT LIKE.
  3122. $records = $DB->get_records_sql($sql, array("%o%"));
  3123. $this->assertCount(3, $records);
  3124. $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false, true, true); // NOT ILIKE.
  3125. $records = $DB->get_records_sql($sql, array("%D%"));
  3126. $this->assertCount(6, $records);
  3127. // Verify usual escaping characters work fine.
  3128. $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, false, '\\');
  3129. $records = $DB->get_records_sql($sql, array("ouc\\_"));
  3130. $this->assertCount(1, $records);
  3131. $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, false, '|');
  3132. $records = $DB->get_records_sql($sql, array("ouc|%"));
  3133. $this->assertCount(1, $records);
  3134. // TODO: we do not require accent insensitivness yet, just make sure it does not throw errors.
  3135. $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, false);
  3136. $records = $DB->get_records_sql($sql, array('aui'));
  3137. // $this->assertEquals(2, count($records), 'Accent insensitive LIKE searches may not be supported in all databases, this is not a problem.');
  3138. $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false, false);
  3139. $records = $DB->get_records_sql($sql, array('aui'));
  3140. // $this->assertEquals(3, count($records), 'Accent insensitive LIKE searches may not be supported in all databases, this is not a problem.');
  3141. }
  3142. public function test_coalesce() {
  3143. $DB = $this->tdb;
  3144. // Testing not-null occurrences, return 1st.
  3145. $sql = "SELECT COALESCE('returnthis', 'orthis', 'orwhynotthis') AS test" . $DB->sql_null_from_clause();
  3146. $this->assertSame('returnthis', $DB->get_field_sql($sql, array()));
  3147. $sql = "SELECT COALESCE(:paramvalue, 'orthis', 'orwhynotthis') AS test" . $DB->sql_null_from_clause();
  3148. $this->assertSame('returnthis', $DB->get_field_sql($sql, array('paramvalue' => 'returnthis')));
  3149. // Testing null occurrences, return 2nd.
  3150. $sql = "SELECT COALESCE(null, 'returnthis', 'orthis') AS test" . $DB->sql_null_from_clause();
  3151. $this->assertSame('returnthis', $DB->get_field_sql($sql, array()));
  3152. $sql = "SELECT COALESCE(:paramvalue, 'returnthis', 'orthis') AS test" . $DB->sql_null_from_clause();
  3153. $this->assertSame('returnthis', $DB->get_field_sql($sql, array('paramvalue' => null)));
  3154. $sql = "SELECT COALESCE(null, :paramvalue, 'orthis') AS test" . $DB->sql_null_from_clause();
  3155. $this->assertSame('returnthis', $DB->get_field_sql($sql, array('paramvalue' => 'returnthis')));
  3156. // Testing null occurrences, return 3rd.
  3157. $sql = "SELECT COALESCE(null, null, 'returnthis') AS test" . $DB->sql_null_from_clause();
  3158. $this->assertSame('returnthis', $DB->get_field_sql($sql, array()));
  3159. $sql = "SELECT COALESCE(null, :paramvalue, 'returnthis') AS test" . $DB->sql_null_from_clause();
  3160. $this->assertSame('returnthis', $DB->get_field_sql($sql, array('paramvalue' => null)));
  3161. $sql = "SELECT COALESCE(null, null, :paramvalue) AS test" . $DB->sql_null_from_clause();
  3162. $this->assertSame('returnthis', $DB->get_field_sql($sql, array('paramvalue' => 'returnthis')));
  3163. // Testing all null occurrences, return null.
  3164. // Note: under mssql, if all elements are nulls, at least one must be a "typed" null, hence
  3165. // we cannot test this in a cross-db way easily, so next 2 tests are using
  3166. // different queries depending of the DB family.
  3167. $customnull = $DB->get_dbfamily() == 'mssql' ? 'CAST(null AS varchar)' : 'null';
  3168. $sql = "SELECT COALESCE(null, null, " . $customnull . ") AS test" . $DB->sql_null_from_clause();
  3169. $this->assertNull($DB->get_field_sql($sql, array()));
  3170. $sql = "SELECT COALESCE(null, :paramvalue, " . $customnull . ") AS test" . $DB->sql_null_from_clause();
  3171. $this->assertNull($DB->get_field_sql($sql, array('paramvalue' => null)));
  3172. // Check there are not problems with whitespace strings.
  3173. $sql = "SELECT COALESCE(null, :paramvalue, null) AS test" . $DB->sql_null_from_clause();
  3174. $this->assertSame('', $DB->get_field_sql($sql, array('paramvalue' => '')));
  3175. }
  3176. public function test_sql_concat() {
  3177. $DB = $this->tdb;
  3178. $dbman = $DB->get_manager();
  3179. // Testing all sort of values.
  3180. $sql = "SELECT ".$DB->sql_concat("?", "?", "?")." AS fullname ". $DB->sql_null_from_clause();
  3181. // String, some unicode chars.
  3182. $params = array('name', 'áéíóú', 'name3');
  3183. $this->assertSame('nameáéíóúname3', $DB->get_field_sql($sql, $params));
  3184. // String, spaces and numbers.
  3185. $params = array('name', ' ', 12345);
  3186. $this->assertSame('name 12345', $DB->get_field_sql($sql, $params));
  3187. // Float, empty and strings.
  3188. $params = array(123.45, '', 'test');
  3189. $this->assertSame('123.45test', $DB->get_field_sql($sql, $params));
  3190. // Only integers.
  3191. $params = array(12, 34, 56);
  3192. $this->assertSame('123456', $DB->get_field_sql($sql, $params));
  3193. // Float, null and strings.
  3194. $params = array(123.45, null, 'test');
  3195. $this->assertNull($DB->get_field_sql($sql, $params)); // Concatenate null with anything result = null.
  3196. // Testing fieldnames + values and also integer fieldnames.
  3197. $table = $this->get_test_table();
  3198. $tablename = $table->getName();
  3199. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3200. $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
  3201. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3202. $dbman->create_table($table);
  3203. $DB->insert_record($tablename, array('description'=>'áéíóú'));
  3204. $DB->insert_record($tablename, array('description'=>'dxxx'));
  3205. $DB->insert_record($tablename, array('description'=>'bcde'));
  3206. // Fieldnames and values mixed.
  3207. $sql = 'SELECT id, ' . $DB->sql_concat('description', "'harcoded'", '?', '?') . ' AS result FROM {' . $tablename . '}';
  3208. $records = $DB->get_records_sql($sql, array(123.45, 'test'));
  3209. $this->assertCount(3, $records);
  3210. $this->assertSame('áéíóúharcoded123.45test', $records[1]->result);
  3211. // Integer fieldnames and values.
  3212. $sql = 'SELECT id, ' . $DB->sql_concat('id', "'harcoded'", '?', '?') . ' AS result FROM {' . $tablename . '}';
  3213. $records = $DB->get_records_sql($sql, array(123.45, 'test'));
  3214. $this->assertCount(3, $records);
  3215. $this->assertSame('1harcoded123.45test', $records[1]->result);
  3216. // All integer fieldnames.
  3217. $sql = 'SELECT id, ' . $DB->sql_concat('id', 'id', 'id') . ' AS result FROM {' . $tablename . '}';
  3218. $records = $DB->get_records_sql($sql, array());
  3219. $this->assertCount(3, $records);
  3220. $this->assertSame('111', $records[1]->result);
  3221. }
  3222. public function test_concat_join() {
  3223. $DB = $this->tdb;
  3224. $sql = "SELECT ".$DB->sql_concat_join("' '", array("?", "?", "?"))." AS fullname ".$DB->sql_null_from_clause();
  3225. $params = array("name", "name2", "name3");
  3226. $result = $DB->get_field_sql($sql, $params);
  3227. $this->assertEquals("name name2 name3", $result);
  3228. }
  3229. public function test_sql_fullname() {
  3230. $DB = $this->tdb;
  3231. $sql = "SELECT ".$DB->sql_fullname(':first', ':last')." AS fullname ".$DB->sql_null_from_clause();
  3232. $params = array('first'=>'Firstname', 'last'=>'Surname');
  3233. $this->assertEquals("Firstname Surname", $DB->get_field_sql($sql, $params));
  3234. }
  3235. public function test_sql_order_by_text() {
  3236. $DB = $this->tdb;
  3237. $dbman = $DB->get_manager();
  3238. $table = $this->get_test_table();
  3239. $tablename = $table->getName();
  3240. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3241. $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
  3242. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3243. $dbman->create_table($table);
  3244. $DB->insert_record($tablename, array('description'=>'abcd'));
  3245. $DB->insert_record($tablename, array('description'=>'dxxx'));
  3246. $DB->insert_record($tablename, array('description'=>'bcde'));
  3247. $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_order_by_text('description');
  3248. $records = $DB->get_records_sql($sql);
  3249. $first = array_shift($records);
  3250. $this->assertEquals(1, $first->id);
  3251. $second = array_shift($records);
  3252. $this->assertEquals(3, $second->id);
  3253. $last = array_shift($records);
  3254. $this->assertEquals(2, $last->id);
  3255. }
  3256. public function test_sql_substring() {
  3257. $DB = $this->tdb;
  3258. $dbman = $DB->get_manager();
  3259. $table = $this->get_test_table();
  3260. $tablename = $table->getName();
  3261. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3262. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
  3263. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3264. $dbman->create_table($table);
  3265. $string = 'abcdefghij';
  3266. $DB->insert_record($tablename, array('name'=>$string));
  3267. $sql = "SELECT id, ".$DB->sql_substr("name", 5)." AS name FROM {{$tablename}}";
  3268. $record = $DB->get_record_sql($sql);
  3269. $this->assertEquals(substr($string, 5-1), $record->name);
  3270. $sql = "SELECT id, ".$DB->sql_substr("name", 5, 2)." AS name FROM {{$tablename}}";
  3271. $record = $DB->get_record_sql($sql);
  3272. $this->assertEquals(substr($string, 5-1, 2), $record->name);
  3273. try {
  3274. // Silence php warning.
  3275. @$DB->sql_substr("name");
  3276. $this->fail("Expecting an exception, none occurred");
  3277. } catch (moodle_exception $e) {
  3278. $this->assertInstanceOf('coding_exception', $e);
  3279. }
  3280. }
  3281. public function test_sql_length() {
  3282. $DB = $this->tdb;
  3283. $this->assertEquals($DB->get_field_sql(
  3284. "SELECT ".$DB->sql_length("'aeiou'").$DB->sql_null_from_clause()), 5);
  3285. $this->assertEquals($DB->get_field_sql(
  3286. "SELECT ".$DB->sql_length("'áéíóú'").$DB->sql_null_from_clause()), 5);
  3287. }
  3288. public function test_sql_position() {
  3289. $DB = $this->tdb;
  3290. $this->assertEquals($DB->get_field_sql(
  3291. "SELECT ".$DB->sql_position("'ood'", "'Moodle'").$DB->sql_null_from_clause()), 2);
  3292. $this->assertEquals($DB->get_field_sql(
  3293. "SELECT ".$DB->sql_position("'Oracle'", "'Moodle'").$DB->sql_null_from_clause()), 0);
  3294. }
  3295. public function test_sql_empty() {
  3296. $DB = $this->tdb;
  3297. $dbman = $DB->get_manager();
  3298. $table = $this->get_test_table();
  3299. $tablename = $table->getName();
  3300. $this->assertSame('', $DB->sql_empty()); // Since 2.5 the hack is applied automatically to all bound params.
  3301. $this->assertDebuggingCalled();
  3302. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3303. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
  3304. $table->add_field('namenotnull', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'default value');
  3305. $table->add_field('namenotnullnodeflt', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
  3306. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3307. $dbman->create_table($table);
  3308. $DB->insert_record($tablename, array('name'=>'', 'namenotnull'=>''));
  3309. $DB->insert_record($tablename, array('name'=>null));
  3310. $DB->insert_record($tablename, array('name'=>'lalala'));
  3311. $DB->insert_record($tablename, array('name'=>0));
  3312. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array(''));
  3313. $this->assertCount(1, $records);
  3314. $record = reset($records);
  3315. $this->assertSame('', $record->name);
  3316. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnull = ?", array(''));
  3317. $this->assertCount(1, $records);
  3318. $record = reset($records);
  3319. $this->assertSame('', $record->namenotnull);
  3320. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnullnodeflt = ?", array(''));
  3321. $this->assertCount(4, $records);
  3322. $record = reset($records);
  3323. $this->assertSame('', $record->namenotnullnodeflt);
  3324. }
  3325. public function test_sql_isempty() {
  3326. $DB = $this->tdb;
  3327. $dbman = $DB->get_manager();
  3328. $table = $this->get_test_table();
  3329. $tablename = $table->getName();
  3330. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3331. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
  3332. $table->add_field('namenull', XMLDB_TYPE_CHAR, '255', null, null, null, null);
  3333. $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
  3334. $table->add_field('descriptionnull', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
  3335. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3336. $dbman->create_table($table);
  3337. $DB->insert_record($tablename, array('name'=>'', 'namenull'=>'', 'description'=>'', 'descriptionnull'=>''));
  3338. $DB->insert_record($tablename, array('name'=>'??', 'namenull'=>null, 'description'=>'??', 'descriptionnull'=>null));
  3339. $DB->insert_record($tablename, array('name'=>'la', 'namenull'=>'la', 'description'=>'la', 'descriptionnull'=>'lalala'));
  3340. $DB->insert_record($tablename, array('name'=>0, 'namenull'=>0, 'description'=>0, 'descriptionnull'=>0));
  3341. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'name', false, false));
  3342. $this->assertCount(1, $records);
  3343. $record = reset($records);
  3344. $this->assertSame('', $record->name);
  3345. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'namenull', true, false));
  3346. $this->assertCount(1, $records);
  3347. $record = reset($records);
  3348. $this->assertSame('', $record->namenull);
  3349. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'description', false, true));
  3350. $this->assertCount(1, $records);
  3351. $record = reset($records);
  3352. $this->assertSame('', $record->description);
  3353. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'descriptionnull', true, true));
  3354. $this->assertCount(1, $records);
  3355. $record = reset($records);
  3356. $this->assertSame('', $record->descriptionnull);
  3357. }
  3358. public function test_sql_isnotempty() {
  3359. $DB = $this->tdb;
  3360. $dbman = $DB->get_manager();
  3361. $table = $this->get_test_table();
  3362. $tablename = $table->getName();
  3363. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3364. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
  3365. $table->add_field('namenull', XMLDB_TYPE_CHAR, '255', null, null, null, null);
  3366. $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
  3367. $table->add_field('descriptionnull', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
  3368. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3369. $dbman->create_table($table);
  3370. $DB->insert_record($tablename, array('name'=>'', 'namenull'=>'', 'description'=>'', 'descriptionnull'=>''));
  3371. $DB->insert_record($tablename, array('name'=>'??', 'namenull'=>null, 'description'=>'??', 'descriptionnull'=>null));
  3372. $DB->insert_record($tablename, array('name'=>'la', 'namenull'=>'la', 'description'=>'la', 'descriptionnull'=>'lalala'));
  3373. $DB->insert_record($tablename, array('name'=>0, 'namenull'=>0, 'description'=>0, 'descriptionnull'=>0));
  3374. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'name', false, false));
  3375. $this->assertCount(3, $records);
  3376. $record = reset($records);
  3377. $this->assertSame('??', $record->name);
  3378. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'namenull', true, false));
  3379. $this->assertCount(2, $records); // Nulls aren't comparable (so they aren't "not empty"). SQL expected behaviour.
  3380. $record = reset($records);
  3381. $this->assertSame('la', $record->namenull); // So 'la' is the first non-empty 'namenull' record.
  3382. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'description', false, true));
  3383. $this->assertCount(3, $records);
  3384. $record = reset($records);
  3385. $this->assertSame('??', $record->description);
  3386. $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'descriptionnull', true, true));
  3387. $this->assertCount(2, $records); // Nulls aren't comparable (so they aren't "not empty"). SQL expected behaviour.
  3388. $record = reset($records);
  3389. $this->assertSame('lalala', $record->descriptionnull); // So 'lalala' is the first non-empty 'descriptionnull' record.
  3390. }
  3391. public function test_sql_regex() {
  3392. $DB = $this->tdb;
  3393. $dbman = $DB->get_manager();
  3394. $table = $this->get_test_table();
  3395. $tablename = $table->getName();
  3396. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3397. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
  3398. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3399. $dbman->create_table($table);
  3400. $DB->insert_record($tablename, array('name'=>'lalala'));
  3401. $DB->insert_record($tablename, array('name'=>'holaaa'));
  3402. $DB->insert_record($tablename, array('name'=>'aouch'));
  3403. $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_regex()." ?";
  3404. $params = array('a$');
  3405. if ($DB->sql_regex_supported()) {
  3406. $records = $DB->get_records_sql($sql, $params);
  3407. $this->assertCount(2, $records);
  3408. } else {
  3409. $this->assertTrue(true, 'Regexp operations not supported. Test skipped');
  3410. }
  3411. $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_regex(false)." ?";
  3412. $params = array('.a');
  3413. if ($DB->sql_regex_supported()) {
  3414. $records = $DB->get_records_sql($sql, $params);
  3415. $this->assertCount(1, $records);
  3416. } else {
  3417. $this->assertTrue(true, 'Regexp operations not supported. Test skipped');
  3418. }
  3419. }
  3420. /**
  3421. * Test some more complex SQL syntax which moodle uses and depends on to work
  3422. * useful to determine if new database libraries can be supported.
  3423. */
  3424. public function test_get_records_sql_complicated() {
  3425. $DB = $this->tdb;
  3426. $dbman = $DB->get_manager();
  3427. $table = $this->get_test_table();
  3428. $tablename = $table->getName();
  3429. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3430. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  3431. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
  3432. $table->add_field('content', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL);
  3433. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3434. $dbman->create_table($table);
  3435. $DB->insert_record($tablename, array('course' => 3, 'content' => 'hello', 'name'=>'xyz'));
  3436. $DB->insert_record($tablename, array('course' => 3, 'content' => 'world', 'name'=>'abc'));
  3437. $DB->insert_record($tablename, array('course' => 5, 'content' => 'hello', 'name'=>'def'));
  3438. $DB->insert_record($tablename, array('course' => 2, 'content' => 'universe', 'name'=>'abc'));
  3439. // Test grouping by expressions in the query. MDL-26819. Note that there are 4 ways:
  3440. // - By column position (GROUP by 1) - Not supported by mssql & oracle
  3441. // - By column name (GROUP by course) - Supported by all, but leading to wrong results
  3442. // - By column alias (GROUP by casecol) - Not supported by mssql & oracle
  3443. // - By complete expression (GROUP BY CASE ...) - 100% cross-db, this test checks it
  3444. $sql = "SELECT (CASE WHEN course = 3 THEN 1 ELSE 0 END) AS casecol,
  3445. COUNT(1) AS countrecs,
  3446. MAX(name) AS maxname
  3447. FROM {{$tablename}}
  3448. GROUP BY CASE WHEN course = 3 THEN 1 ELSE 0 END
  3449. ORDER BY casecol DESC";
  3450. $result = array(
  3451. 1 => (object)array('casecol' => 1, 'countrecs' => 2, 'maxname' => 'xyz'),
  3452. 0 => (object)array('casecol' => 0, 'countrecs' => 2, 'maxname' => 'def'));
  3453. $records = $DB->get_records_sql($sql, null);
  3454. $this->assertEquals($result, $records);
  3455. // Another grouping by CASE expression just to ensure it works ok for multiple WHEN.
  3456. $sql = "SELECT CASE name
  3457. WHEN 'xyz' THEN 'last'
  3458. WHEN 'def' THEN 'mid'
  3459. WHEN 'abc' THEN 'first'
  3460. END AS casecol,
  3461. COUNT(1) AS countrecs,
  3462. MAX(name) AS maxname
  3463. FROM {{$tablename}}
  3464. GROUP BY CASE name
  3465. WHEN 'xyz' THEN 'last'
  3466. WHEN 'def' THEN 'mid'
  3467. WHEN 'abc' THEN 'first'
  3468. END
  3469. ORDER BY casecol DESC";
  3470. $result = array(
  3471. 'mid' => (object)array('casecol' => 'mid', 'countrecs' => 1, 'maxname' => 'def'),
  3472. 'last' => (object)array('casecol' => 'last', 'countrecs' => 1, 'maxname' => 'xyz'),
  3473. 'first'=> (object)array('casecol' => 'first', 'countrecs' => 2, 'maxname' => 'abc'));
  3474. $records = $DB->get_records_sql($sql, null);
  3475. $this->assertEquals($result, $records);
  3476. // Test CASE expressions in the ORDER BY clause - used by MDL-34657.
  3477. $sql = "SELECT id, course, name
  3478. FROM {{$tablename}}
  3479. ORDER BY CASE WHEN (course = 5 OR name = 'xyz') THEN 0 ELSE 1 END, name, course";
  3480. // First, records matching the course = 5 OR name = 'xyz', then the rest. Each.
  3481. // group ordered by name and course.
  3482. $result = array(
  3483. 3 => (object)array('id' => 3, 'course' => 5, 'name' => 'def'),
  3484. 1 => (object)array('id' => 1, 'course' => 3, 'name' => 'xyz'),
  3485. 4 => (object)array('id' => 4, 'course' => 2, 'name' => 'abc'),
  3486. 2 => (object)array('id' => 2, 'course' => 3, 'name' => 'abc'));
  3487. $records = $DB->get_records_sql($sql, null);
  3488. $this->assertEquals($result, $records);
  3489. // Verify also array keys, order is important in this test.
  3490. $this->assertEquals(array_keys($result), array_keys($records));
  3491. // Test limits in queries with DISTINCT/ALL clauses and multiple whitespace. MDL-25268.
  3492. $sql = "SELECT DISTINCT course
  3493. FROM {{$tablename}}
  3494. ORDER BY course";
  3495. // Only limitfrom.
  3496. $records = $DB->get_records_sql($sql, null, 1);
  3497. $this->assertCount(2, $records);
  3498. $this->assertEquals(3, reset($records)->course);
  3499. $this->assertEquals(5, next($records)->course);
  3500. // Only limitnum.
  3501. $records = $DB->get_records_sql($sql, null, 0, 2);
  3502. $this->assertCount(2, $records);
  3503. $this->assertEquals(2, reset($records)->course);
  3504. $this->assertEquals(3, next($records)->course);
  3505. // Both limitfrom and limitnum.
  3506. $records = $DB->get_records_sql($sql, null, 2, 2);
  3507. $this->assertCount(1, $records);
  3508. $this->assertEquals(5, reset($records)->course);
  3509. // We have sql like this in moodle, this syntax breaks on older versions of sqlite for example..
  3510. $sql = "SELECT a.id AS id, a.course AS course
  3511. FROM {{$tablename}} a
  3512. JOIN (SELECT * FROM {{$tablename}}) b ON a.id = b.id
  3513. WHERE a.course = ?";
  3514. $records = $DB->get_records_sql($sql, array(3));
  3515. $this->assertCount(2, $records);
  3516. $this->assertEquals(1, reset($records)->id);
  3517. $this->assertEquals(2, next($records)->id);
  3518. // Do NOT try embedding sql_xxxx() helper functions in conditions array of count_records(), they don't break params/binding!
  3519. $count = $DB->count_records_select($tablename, "course = :course AND ".$DB->sql_compare_text('content')." = :content", array('course' => 3, 'content' => 'hello'));
  3520. $this->assertEquals(1, $count);
  3521. // Test int x string comparison.
  3522. $sql = "SELECT *
  3523. FROM {{$tablename}} c
  3524. WHERE name = ?";
  3525. $this->assertCount(0, $DB->get_records_sql($sql, array(10)));
  3526. $this->assertCount(0, $DB->get_records_sql($sql, array("10")));
  3527. $DB->insert_record($tablename, array('course' => 7, 'content' => 'xx', 'name'=>'1'));
  3528. $DB->insert_record($tablename, array('course' => 7, 'content' => 'yy', 'name'=>'2'));
  3529. $this->assertCount(1, $DB->get_records_sql($sql, array(1)));
  3530. $this->assertCount(1, $DB->get_records_sql($sql, array("1")));
  3531. $this->assertCount(0, $DB->get_records_sql($sql, array(10)));
  3532. $this->assertCount(0, $DB->get_records_sql($sql, array("10")));
  3533. $DB->insert_record($tablename, array('course' => 7, 'content' => 'xx', 'name'=>'1abc'));
  3534. $this->assertCount(1, $DB->get_records_sql($sql, array(1)));
  3535. $this->assertCount(1, $DB->get_records_sql($sql, array("1")));
  3536. // Test get_in_or_equal() with a big number of elements. Note that ideally
  3537. // we should be detecting and warning about any use over, say, 200 elements
  3538. // And recommend to change code to use subqueries and/or chunks instead.
  3539. $currentcount = $DB->count_records($tablename);
  3540. $numelements = 10000; // Verify that we can handle 10000 elements (crazy!)
  3541. $values = range(1, $numelements);
  3542. list($insql, $inparams) = $DB->get_in_or_equal($values, SQL_PARAMS_QM); // With QM params.
  3543. $sql = "SELECT *
  3544. FROM {{$tablename}}
  3545. WHERE id $insql";
  3546. $results = $DB->get_records_sql($sql, $inparams);
  3547. $this->assertCount($currentcount, $results);
  3548. list($insql, $inparams) = $DB->get_in_or_equal($values, SQL_PARAMS_NAMED); // With NAMED params.
  3549. $sql = "SELECT *
  3550. FROM {{$tablename}}
  3551. WHERE id $insql";
  3552. $results = $DB->get_records_sql($sql, $inparams);
  3553. $this->assertCount($currentcount, $results);
  3554. }
  3555. public function test_replace_all_text() {
  3556. $DB = $this->tdb;
  3557. $dbman = $DB->get_manager();
  3558. if (!$DB->replace_all_text_supported()) {
  3559. $this->markTestSkipped($DB->get_name().' does not support replacing of texts');
  3560. }
  3561. $table = $this->get_test_table();
  3562. $tablename = $table->getName();
  3563. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3564. $table->add_field('name', XMLDB_TYPE_CHAR, '20', null, null);
  3565. $table->add_field('intro', XMLDB_TYPE_TEXT, 'big', null, null);
  3566. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3567. $dbman->create_table($table);
  3568. $id1 = (string)$DB->insert_record($tablename, array('name' => null, 'intro' => null));
  3569. $id2 = (string)$DB->insert_record($tablename, array('name' => '', 'intro' => ''));
  3570. $id3 = (string)$DB->insert_record($tablename, array('name' => 'xxyy', 'intro' => 'vvzz'));
  3571. $id4 = (string)$DB->insert_record($tablename, array('name' => 'aa bb aa bb', 'intro' => 'cc dd cc aa'));
  3572. $id5 = (string)$DB->insert_record($tablename, array('name' => 'kkllll', 'intro' => 'kkllll'));
  3573. $expected = $DB->get_records($tablename, array(), 'id ASC');
  3574. $columns = $DB->get_columns($tablename);
  3575. $DB->replace_all_text($tablename, $columns['name'], 'aa', 'o');
  3576. $result = $DB->get_records($tablename, array(), 'id ASC');
  3577. $expected[$id4]->name = 'o bb o bb';
  3578. $this->assertEquals($expected, $result);
  3579. $DB->replace_all_text($tablename, $columns['intro'], 'aa', 'o');
  3580. $result = $DB->get_records($tablename, array(), 'id ASC');
  3581. $expected[$id4]->intro = 'cc dd cc o';
  3582. $this->assertEquals($expected, $result);
  3583. $DB->replace_all_text($tablename, $columns['name'], '_', '*');
  3584. $DB->replace_all_text($tablename, $columns['name'], '?', '*');
  3585. $DB->replace_all_text($tablename, $columns['name'], '%', '*');
  3586. $DB->replace_all_text($tablename, $columns['intro'], '_', '*');
  3587. $DB->replace_all_text($tablename, $columns['intro'], '?', '*');
  3588. $DB->replace_all_text($tablename, $columns['intro'], '%', '*');
  3589. $result = $DB->get_records($tablename, array(), 'id ASC');
  3590. $this->assertEquals($expected, $result);
  3591. $long = '1234567890123456789';
  3592. $DB->replace_all_text($tablename, $columns['name'], 'kk', $long);
  3593. $result = $DB->get_records($tablename, array(), 'id ASC');
  3594. $expected[$id5]->name = core_text::substr($long.'llll', 0, 20);
  3595. $this->assertEquals($expected, $result);
  3596. $DB->replace_all_text($tablename, $columns['intro'], 'kk', $long);
  3597. $result = $DB->get_records($tablename, array(), 'id ASC');
  3598. $expected[$id5]->intro = $long.'llll';
  3599. $this->assertEquals($expected, $result);
  3600. }
  3601. public function test_onelevel_commit() {
  3602. $DB = $this->tdb;
  3603. $dbman = $DB->get_manager();
  3604. $table = $this->get_test_table();
  3605. $tablename = $table->getName();
  3606. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3607. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  3608. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3609. $dbman->create_table($table);
  3610. $transaction = $DB->start_delegated_transaction();
  3611. $data = (object)array('course'=>3);
  3612. $this->assertEquals(0, $DB->count_records($tablename));
  3613. $DB->insert_record($tablename, $data);
  3614. $this->assertEquals(1, $DB->count_records($tablename));
  3615. $transaction->allow_commit();
  3616. $this->assertEquals(1, $DB->count_records($tablename));
  3617. }
  3618. public function test_transaction_ignore_error_trouble() {
  3619. $DB = $this->tdb;
  3620. $dbman = $DB->get_manager();
  3621. $table = $this->get_test_table();
  3622. $tablename = $table->getName();
  3623. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3624. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  3625. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3626. $table->add_index('course', XMLDB_INDEX_UNIQUE, array('course'));
  3627. $dbman->create_table($table);
  3628. // Test error on SQL_QUERY_INSERT.
  3629. $transaction = $DB->start_delegated_transaction();
  3630. $this->assertEquals(0, $DB->count_records($tablename));
  3631. $DB->insert_record($tablename, (object)array('course'=>1));
  3632. $this->assertEquals(1, $DB->count_records($tablename));
  3633. try {
  3634. $DB->insert_record($tablename, (object)array('course'=>1));
  3635. } catch (Exception $e) {
  3636. // This must be ignored and it must not roll back the whole transaction.
  3637. }
  3638. $DB->insert_record($tablename, (object)array('course'=>2));
  3639. $this->assertEquals(2, $DB->count_records($tablename));
  3640. $transaction->allow_commit();
  3641. $this->assertEquals(2, $DB->count_records($tablename));
  3642. $this->assertFalse($DB->is_transaction_started());
  3643. // Test error on SQL_QUERY_SELECT.
  3644. $DB->delete_records($tablename);
  3645. $transaction = $DB->start_delegated_transaction();
  3646. $this->assertEquals(0, $DB->count_records($tablename));
  3647. $DB->insert_record($tablename, (object)array('course'=>1));
  3648. $this->assertEquals(1, $DB->count_records($tablename));
  3649. try {
  3650. $DB->get_records_sql('s e l e c t');
  3651. } catch (moodle_exception $e) {
  3652. // This must be ignored and it must not roll back the whole transaction.
  3653. }
  3654. $DB->insert_record($tablename, (object)array('course'=>2));
  3655. $this->assertEquals(2, $DB->count_records($tablename));
  3656. $transaction->allow_commit();
  3657. $this->assertEquals(2, $DB->count_records($tablename));
  3658. $this->assertFalse($DB->is_transaction_started());
  3659. // Test error on structure SQL_QUERY_UPDATE.
  3660. $DB->delete_records($tablename);
  3661. $transaction = $DB->start_delegated_transaction();
  3662. $this->assertEquals(0, $DB->count_records($tablename));
  3663. $DB->insert_record($tablename, (object)array('course'=>1));
  3664. $this->assertEquals(1, $DB->count_records($tablename));
  3665. try {
  3666. $DB->execute('xxxx');
  3667. } catch (moodle_exception $e) {
  3668. // This must be ignored and it must not roll back the whole transaction.
  3669. }
  3670. $DB->insert_record($tablename, (object)array('course'=>2));
  3671. $this->assertEquals(2, $DB->count_records($tablename));
  3672. $transaction->allow_commit();
  3673. $this->assertEquals(2, $DB->count_records($tablename));
  3674. $this->assertFalse($DB->is_transaction_started());
  3675. // Test error on structure SQL_QUERY_STRUCTURE.
  3676. $DB->delete_records($tablename);
  3677. $transaction = $DB->start_delegated_transaction();
  3678. $this->assertEquals(0, $DB->count_records($tablename));
  3679. $DB->insert_record($tablename, (object)array('course'=>1));
  3680. $this->assertEquals(1, $DB->count_records($tablename));
  3681. try {
  3682. $DB->change_database_structure('xxxx');
  3683. } catch (moodle_exception $e) {
  3684. // This must be ignored and it must not roll back the whole transaction.
  3685. }
  3686. $DB->insert_record($tablename, (object)array('course'=>2));
  3687. $this->assertEquals(2, $DB->count_records($tablename));
  3688. $transaction->allow_commit();
  3689. $this->assertEquals(2, $DB->count_records($tablename));
  3690. $this->assertFalse($DB->is_transaction_started());
  3691. // NOTE: SQL_QUERY_STRUCTURE is intentionally not tested here because it should never fail.
  3692. }
  3693. public function test_onelevel_rollback() {
  3694. $DB = $this->tdb;
  3695. $dbman = $DB->get_manager();
  3696. $table = $this->get_test_table();
  3697. $tablename = $table->getName();
  3698. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3699. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  3700. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3701. $dbman->create_table($table);
  3702. // This might in fact encourage ppl to migrate from myisam to innodb.
  3703. $transaction = $DB->start_delegated_transaction();
  3704. $data = (object)array('course'=>3);
  3705. $this->assertEquals(0, $DB->count_records($tablename));
  3706. $DB->insert_record($tablename, $data);
  3707. $this->assertEquals(1, $DB->count_records($tablename));
  3708. try {
  3709. $transaction->rollback(new Exception('test'));
  3710. $this->fail('transaction rollback must rethrow exception');
  3711. } catch (Exception $e) {
  3712. // Ignored.
  3713. }
  3714. $this->assertEquals(0, $DB->count_records($tablename));
  3715. }
  3716. public function test_nested_transactions() {
  3717. $DB = $this->tdb;
  3718. $dbman = $DB->get_manager();
  3719. $table = $this->get_test_table();
  3720. $tablename = $table->getName();
  3721. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3722. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  3723. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3724. $dbman->create_table($table);
  3725. // Two level commit.
  3726. $this->assertFalse($DB->is_transaction_started());
  3727. $transaction1 = $DB->start_delegated_transaction();
  3728. $this->assertTrue($DB->is_transaction_started());
  3729. $data = (object)array('course'=>3);
  3730. $DB->insert_record($tablename, $data);
  3731. $transaction2 = $DB->start_delegated_transaction();
  3732. $data = (object)array('course'=>4);
  3733. $DB->insert_record($tablename, $data);
  3734. $transaction2->allow_commit();
  3735. $this->assertTrue($DB->is_transaction_started());
  3736. $transaction1->allow_commit();
  3737. $this->assertFalse($DB->is_transaction_started());
  3738. $this->assertEquals(2, $DB->count_records($tablename));
  3739. $DB->delete_records($tablename);
  3740. // Rollback from top level.
  3741. $transaction1 = $DB->start_delegated_transaction();
  3742. $data = (object)array('course'=>3);
  3743. $DB->insert_record($tablename, $data);
  3744. $transaction2 = $DB->start_delegated_transaction();
  3745. $data = (object)array('course'=>4);
  3746. $DB->insert_record($tablename, $data);
  3747. $transaction2->allow_commit();
  3748. try {
  3749. $transaction1->rollback(new Exception('test'));
  3750. $this->fail('transaction rollback must rethrow exception');
  3751. } catch (Exception $e) {
  3752. $this->assertEquals(get_class($e), 'Exception');
  3753. }
  3754. $this->assertEquals(0, $DB->count_records($tablename));
  3755. $DB->delete_records($tablename);
  3756. // Rollback from nested level.
  3757. $transaction1 = $DB->start_delegated_transaction();
  3758. $data = (object)array('course'=>3);
  3759. $DB->insert_record($tablename, $data);
  3760. $transaction2 = $DB->start_delegated_transaction();
  3761. $data = (object)array('course'=>4);
  3762. $DB->insert_record($tablename, $data);
  3763. try {
  3764. $transaction2->rollback(new Exception('test'));
  3765. $this->fail('transaction rollback must rethrow exception');
  3766. } catch (Exception $e) {
  3767. $this->assertEquals(get_class($e), 'Exception');
  3768. }
  3769. $this->assertEquals(2, $DB->count_records($tablename)); // Not rolled back yet.
  3770. try {
  3771. $transaction1->allow_commit();
  3772. } catch (moodle_exception $e) {
  3773. $this->assertInstanceOf('dml_transaction_exception', $e);
  3774. }
  3775. $this->assertEquals(2, $DB->count_records($tablename)); // Not rolled back yet.
  3776. // The forced rollback is done from the default_exception handler and similar places,
  3777. // let's do it manually here.
  3778. $this->assertTrue($DB->is_transaction_started());
  3779. $DB->force_transaction_rollback();
  3780. $this->assertFalse($DB->is_transaction_started());
  3781. $this->assertEquals(0, $DB->count_records($tablename)); // Finally rolled back.
  3782. $DB->delete_records($tablename);
  3783. // Test interactions of recordset and transactions - this causes problems in SQL Server.
  3784. $table2 = $this->get_test_table('2');
  3785. $tablename2 = $table2->getName();
  3786. $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3787. $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  3788. $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3789. $dbman->create_table($table2);
  3790. $DB->insert_record($tablename, array('course'=>1));
  3791. $DB->insert_record($tablename, array('course'=>2));
  3792. $DB->insert_record($tablename, array('course'=>3));
  3793. $DB->insert_record($tablename2, array('course'=>5));
  3794. $DB->insert_record($tablename2, array('course'=>6));
  3795. $DB->insert_record($tablename2, array('course'=>7));
  3796. $DB->insert_record($tablename2, array('course'=>8));
  3797. $rs1 = $DB->get_recordset($tablename);
  3798. $i = 0;
  3799. foreach ($rs1 as $record1) {
  3800. $i++;
  3801. $rs2 = $DB->get_recordset($tablename2);
  3802. $j = 0;
  3803. foreach ($rs2 as $record2) {
  3804. $t = $DB->start_delegated_transaction();
  3805. $DB->set_field($tablename, 'course', $record1->course+1, array('id'=>$record1->id));
  3806. $DB->set_field($tablename2, 'course', $record2->course+1, array('id'=>$record2->id));
  3807. $t->allow_commit();
  3808. $j++;
  3809. }
  3810. $rs2->close();
  3811. $this->assertEquals(4, $j);
  3812. }
  3813. $rs1->close();
  3814. $this->assertEquals(3, $i);
  3815. // Test nested recordsets isolation without transaction.
  3816. $DB->delete_records($tablename);
  3817. $DB->insert_record($tablename, array('course'=>1));
  3818. $DB->insert_record($tablename, array('course'=>2));
  3819. $DB->insert_record($tablename, array('course'=>3));
  3820. $DB->delete_records($tablename2);
  3821. $DB->insert_record($tablename2, array('course'=>5));
  3822. $DB->insert_record($tablename2, array('course'=>6));
  3823. $DB->insert_record($tablename2, array('course'=>7));
  3824. $DB->insert_record($tablename2, array('course'=>8));
  3825. $rs1 = $DB->get_recordset($tablename);
  3826. $i = 0;
  3827. foreach ($rs1 as $record1) {
  3828. $i++;
  3829. $rs2 = $DB->get_recordset($tablename2);
  3830. $j = 0;
  3831. foreach ($rs2 as $record2) {
  3832. $DB->set_field($tablename, 'course', $record1->course+1, array('id'=>$record1->id));
  3833. $DB->set_field($tablename2, 'course', $record2->course+1, array('id'=>$record2->id));
  3834. $j++;
  3835. }
  3836. $rs2->close();
  3837. $this->assertEquals(4, $j);
  3838. }
  3839. $rs1->close();
  3840. $this->assertEquals(3, $i);
  3841. }
  3842. public function test_transactions_forbidden() {
  3843. $DB = $this->tdb;
  3844. $dbman = $DB->get_manager();
  3845. $table = $this->get_test_table();
  3846. $tablename = $table->getName();
  3847. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3848. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  3849. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3850. $dbman->create_table($table);
  3851. $DB->transactions_forbidden();
  3852. $transaction = $DB->start_delegated_transaction();
  3853. $data = (object)array('course'=>1);
  3854. $DB->insert_record($tablename, $data);
  3855. try {
  3856. $DB->transactions_forbidden();
  3857. } catch (moodle_exception $e) {
  3858. $this->assertInstanceOf('dml_transaction_exception', $e);
  3859. }
  3860. // The previous test does not force rollback.
  3861. $transaction->allow_commit();
  3862. $this->assertFalse($DB->is_transaction_started());
  3863. $this->assertEquals(1, $DB->count_records($tablename));
  3864. }
  3865. public function test_wrong_transactions() {
  3866. $DB = $this->tdb;
  3867. $dbman = $DB->get_manager();
  3868. $table = $this->get_test_table();
  3869. $tablename = $table->getName();
  3870. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3871. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  3872. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3873. $dbman->create_table($table);
  3874. // Wrong order of nested commits.
  3875. $transaction1 = $DB->start_delegated_transaction();
  3876. $data = (object)array('course'=>3);
  3877. $DB->insert_record($tablename, $data);
  3878. $transaction2 = $DB->start_delegated_transaction();
  3879. $data = (object)array('course'=>4);
  3880. $DB->insert_record($tablename, $data);
  3881. try {
  3882. $transaction1->allow_commit();
  3883. $this->fail('wrong order of commits must throw exception');
  3884. } catch (moodle_exception $e) {
  3885. $this->assertInstanceOf('dml_transaction_exception', $e);
  3886. }
  3887. try {
  3888. $transaction2->allow_commit();
  3889. $this->fail('first wrong commit forces rollback');
  3890. } catch (moodle_exception $e) {
  3891. $this->assertInstanceOf('dml_transaction_exception', $e);
  3892. }
  3893. // This is done in default exception handler usually.
  3894. $this->assertTrue($DB->is_transaction_started());
  3895. $this->assertEquals(2, $DB->count_records($tablename)); // Not rolled back yet.
  3896. $DB->force_transaction_rollback();
  3897. $this->assertEquals(0, $DB->count_records($tablename));
  3898. $DB->delete_records($tablename);
  3899. // Wrong order of nested rollbacks.
  3900. $transaction1 = $DB->start_delegated_transaction();
  3901. $data = (object)array('course'=>3);
  3902. $DB->insert_record($tablename, $data);
  3903. $transaction2 = $DB->start_delegated_transaction();
  3904. $data = (object)array('course'=>4);
  3905. $DB->insert_record($tablename, $data);
  3906. try {
  3907. // This first rollback should prevent all other rollbacks.
  3908. $transaction1->rollback(new Exception('test'));
  3909. } catch (Exception $e) {
  3910. $this->assertEquals(get_class($e), 'Exception');
  3911. }
  3912. try {
  3913. $transaction2->rollback(new Exception('test'));
  3914. } catch (Exception $e) {
  3915. $this->assertEquals(get_class($e), 'Exception');
  3916. }
  3917. try {
  3918. $transaction1->rollback(new Exception('test'));
  3919. } catch (moodle_exception $e) {
  3920. $this->assertInstanceOf('dml_transaction_exception', $e);
  3921. }
  3922. // This is done in default exception handler usually.
  3923. $this->assertTrue($DB->is_transaction_started());
  3924. $DB->force_transaction_rollback();
  3925. $DB->delete_records($tablename);
  3926. // Unknown transaction object.
  3927. $transaction1 = $DB->start_delegated_transaction();
  3928. $data = (object)array('course'=>3);
  3929. $DB->insert_record($tablename, $data);
  3930. $transaction2 = new moodle_transaction($DB);
  3931. try {
  3932. $transaction2->allow_commit();
  3933. $this->fail('foreign transaction must fail');
  3934. } catch (moodle_exception $e) {
  3935. $this->assertInstanceOf('dml_transaction_exception', $e);
  3936. }
  3937. try {
  3938. $transaction1->allow_commit();
  3939. $this->fail('first wrong commit forces rollback');
  3940. } catch (moodle_exception $e) {
  3941. $this->assertInstanceOf('dml_transaction_exception', $e);
  3942. }
  3943. $DB->force_transaction_rollback();
  3944. $DB->delete_records($tablename);
  3945. }
  3946. public function test_concurent_transactions() {
  3947. // Notes about this test:
  3948. // 1- MySQL needs to use one engine with transactions support (InnoDB).
  3949. // 2- MSSQL needs to have enabled versioning for read committed
  3950. // transactions (ALTER DATABASE xxx SET READ_COMMITTED_SNAPSHOT ON)
  3951. $DB = $this->tdb;
  3952. $dbman = $DB->get_manager();
  3953. $table = $this->get_test_table();
  3954. $tablename = $table->getName();
  3955. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  3956. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  3957. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  3958. $dbman->create_table($table);
  3959. $transaction = $DB->start_delegated_transaction();
  3960. $data = (object)array('course'=>1);
  3961. $this->assertEquals(0, $DB->count_records($tablename));
  3962. $DB->insert_record($tablename, $data);
  3963. $this->assertEquals(1, $DB->count_records($tablename));
  3964. // Open second connection.
  3965. $cfg = $DB->export_dbconfig();
  3966. if (!isset($cfg->dboptions)) {
  3967. $cfg->dboptions = array();
  3968. }
  3969. $DB2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
  3970. $DB2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
  3971. // Second instance should not see pending inserts.
  3972. $this->assertEquals(0, $DB2->count_records($tablename));
  3973. $data = (object)array('course'=>2);
  3974. $DB2->insert_record($tablename, $data);
  3975. $this->assertEquals(1, $DB2->count_records($tablename));
  3976. // First should see the changes done from second.
  3977. $this->assertEquals(2, $DB->count_records($tablename));
  3978. // Now commit and we should see it finally in second connections.
  3979. $transaction->allow_commit();
  3980. $this->assertEquals(2, $DB2->count_records($tablename));
  3981. // Let's try delete all is also working on (this checks MDL-29198).
  3982. // Initially both connections see all the records in the table (2).
  3983. $this->assertEquals(2, $DB->count_records($tablename));
  3984. $this->assertEquals(2, $DB2->count_records($tablename));
  3985. $transaction = $DB->start_delegated_transaction();
  3986. // Delete all from within transaction.
  3987. $DB->delete_records($tablename);
  3988. // Transactional $DB, sees 0 records now.
  3989. $this->assertEquals(0, $DB->count_records($tablename));
  3990. // Others ($DB2) get no changes yet.
  3991. $this->assertEquals(2, $DB2->count_records($tablename));
  3992. // Now commit and we should see changes.
  3993. $transaction->allow_commit();
  3994. $this->assertEquals(0, $DB2->count_records($tablename));
  3995. $DB2->dispose();
  3996. }
  3997. public function test_session_locks() {
  3998. $DB = $this->tdb;
  3999. $dbman = $DB->get_manager();
  4000. // Open second connection.
  4001. $cfg = $DB->export_dbconfig();
  4002. if (!isset($cfg->dboptions)) {
  4003. $cfg->dboptions = array();
  4004. }
  4005. $DB2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
  4006. $DB2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
  4007. // Testing that acquiring a lock effectively locks.
  4008. // Get a session lock on connection1.
  4009. $rowid = rand(100, 200);
  4010. $timeout = 1;
  4011. $DB->get_session_lock($rowid, $timeout);
  4012. // Try to get the same session lock on connection2.
  4013. try {
  4014. $DB2->get_session_lock($rowid, $timeout);
  4015. $DB2->release_session_lock($rowid); // Should not be executed, but here for safety.
  4016. $this->fail('An Exception is missing, expected due to session lock acquired.');
  4017. } catch (moodle_exception $e) {
  4018. $this->assertInstanceOf('dml_sessionwait_exception', $e);
  4019. $DB->release_session_lock($rowid); // Release lock on connection1.
  4020. }
  4021. // Testing that releasing a lock effectively frees.
  4022. // Get a session lock on connection1.
  4023. $rowid = rand(100, 200);
  4024. $timeout = 1;
  4025. $DB->get_session_lock($rowid, $timeout);
  4026. // Release the lock on connection1.
  4027. $DB->release_session_lock($rowid);
  4028. // Get the just released lock on connection2.
  4029. $DB2->get_session_lock($rowid, $timeout);
  4030. // Release the lock on connection2.
  4031. $DB2->release_session_lock($rowid);
  4032. $DB2->dispose();
  4033. }
  4034. public function test_bound_param_types() {
  4035. $DB = $this->tdb;
  4036. $dbman = $DB->get_manager();
  4037. $table = $this->get_test_table();
  4038. $tablename = $table->getName();
  4039. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  4040. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
  4041. $table->add_field('content', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL);
  4042. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  4043. $dbman->create_table($table);
  4044. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => '1', 'content'=>'xx')));
  4045. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 2, 'content'=>'yy')));
  4046. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'somestring', 'content'=>'zz')));
  4047. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'aa', 'content'=>'1')));
  4048. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'bb', 'content'=>2)));
  4049. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'cc', 'content'=>'sometext')));
  4050. // Conditions in CHAR columns.
  4051. $this->assertTrue($DB->record_exists($tablename, array('name'=>1)));
  4052. $this->assertTrue($DB->record_exists($tablename, array('name'=>'1')));
  4053. $this->assertFalse($DB->record_exists($tablename, array('name'=>111)));
  4054. $this->assertNotEmpty($DB->get_record($tablename, array('name'=>1)));
  4055. $this->assertNotEmpty($DB->get_record($tablename, array('name'=>'1')));
  4056. $this->assertEmpty($DB->get_record($tablename, array('name'=>111)));
  4057. $sqlqm = "SELECT *
  4058. FROM {{$tablename}}
  4059. WHERE name = ?";
  4060. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, array(1)));
  4061. $this->assertCount(1, $records);
  4062. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, array('1')));
  4063. $this->assertCount(1, $records);
  4064. $records = $DB->get_records_sql($sqlqm, array(222));
  4065. $this->assertCount(0, $records);
  4066. $sqlnamed = "SELECT *
  4067. FROM {{$tablename}}
  4068. WHERE name = :name";
  4069. $this->assertNotEmpty($records = $DB->get_records_sql($sqlnamed, array('name' => 2)));
  4070. $this->assertCount(1, $records);
  4071. $this->assertNotEmpty($records = $DB->get_records_sql($sqlnamed, array('name' => '2')));
  4072. $this->assertCount(1, $records);
  4073. // Conditions in TEXT columns always must be performed with the sql_compare_text
  4074. // helper function on both sides of the condition.
  4075. $sqlqm = "SELECT *
  4076. FROM {{$tablename}}
  4077. WHERE " . $DB->sql_compare_text('content') . " = " . $DB->sql_compare_text('?');
  4078. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, array('1')));
  4079. $this->assertCount(1, $records);
  4080. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, array(1)));
  4081. $this->assertCount(1, $records);
  4082. $sqlnamed = "SELECT *
  4083. FROM {{$tablename}}
  4084. WHERE " . $DB->sql_compare_text('content') . " = " . $DB->sql_compare_text(':content');
  4085. $this->assertNotEmpty($records = $DB->get_records_sql($sqlnamed, array('content' => 2)));
  4086. $this->assertCount(1, $records);
  4087. $this->assertNotEmpty($records = $DB->get_records_sql($sqlnamed, array('content' => '2')));
  4088. $this->assertCount(1, $records);
  4089. }
  4090. public function test_bound_param_reserved() {
  4091. $DB = $this->tdb;
  4092. $dbman = $DB->get_manager();
  4093. $table = $this->get_test_table();
  4094. $tablename = $table->getName();
  4095. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  4096. $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  4097. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  4098. $dbman->create_table($table);
  4099. $DB->insert_record($tablename, array('course' => '1'));
  4100. // Make sure reserved words do not cause fatal problems in query parameters.
  4101. $DB->execute("UPDATE {{$tablename}} SET course = 1 WHERE id = :select", array('select'=>1));
  4102. $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE course = :select", array('select'=>1));
  4103. $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} WHERE course = :select", array('select'=>1));
  4104. $rs->close();
  4105. $DB->get_fieldset_sql("SELECT id FROM {{$tablename}} WHERE course = :select", array('select'=>1));
  4106. $DB->set_field_select($tablename, 'course', '1', "id = :select", array('select'=>1));
  4107. $DB->delete_records_select($tablename, "id = :select", array('select'=>1));
  4108. // If we get here test passed ok.
  4109. $this->assertTrue(true);
  4110. }
  4111. public function test_limits_and_offsets() {
  4112. $DB = $this->tdb;
  4113. $dbman = $DB->get_manager();
  4114. $table = $this->get_test_table();
  4115. $tablename = $table->getName();
  4116. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  4117. $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
  4118. $table->add_field('content', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL);
  4119. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  4120. $dbman->create_table($table);
  4121. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'a', 'content'=>'one')));
  4122. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'b', 'content'=>'two')));
  4123. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'c', 'content'=>'three')));
  4124. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'd', 'content'=>'four')));
  4125. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'e', 'content'=>'five')));
  4126. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'f', 'content'=>'six')));
  4127. $sqlqm = "SELECT *
  4128. FROM {{$tablename}}";
  4129. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 4));
  4130. $this->assertCount(2, $records);
  4131. $this->assertSame('e', reset($records)->name);
  4132. $this->assertSame('f', end($records)->name);
  4133. $sqlqm = "SELECT *
  4134. FROM {{$tablename}}";
  4135. $this->assertEmpty($records = $DB->get_records_sql($sqlqm, null, 8));
  4136. $sqlqm = "SELECT *
  4137. FROM {{$tablename}}";
  4138. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 0, 4));
  4139. $this->assertCount(4, $records);
  4140. $this->assertSame('a', reset($records)->name);
  4141. $this->assertSame('d', end($records)->name);
  4142. $sqlqm = "SELECT *
  4143. FROM {{$tablename}}";
  4144. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 0, 8));
  4145. $this->assertCount(6, $records);
  4146. $this->assertSame('a', reset($records)->name);
  4147. $this->assertSame('f', end($records)->name);
  4148. $sqlqm = "SELECT *
  4149. FROM {{$tablename}}";
  4150. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 1, 4));
  4151. $this->assertCount(4, $records);
  4152. $this->assertSame('b', reset($records)->name);
  4153. $this->assertSame('e', end($records)->name);
  4154. $sqlqm = "SELECT *
  4155. FROM {{$tablename}}";
  4156. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 4, 4));
  4157. $this->assertCount(2, $records);
  4158. $this->assertSame('e', reset($records)->name);
  4159. $this->assertSame('f', end($records)->name);
  4160. $sqlqm = "SELECT t.*, t.name AS test
  4161. FROM {{$tablename}} t
  4162. ORDER BY t.id ASC";
  4163. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 4, 4));
  4164. $this->assertCount(2, $records);
  4165. $this->assertSame('e', reset($records)->name);
  4166. $this->assertSame('f', end($records)->name);
  4167. $sqlqm = "SELECT DISTINCT t.name, t.name AS test
  4168. FROM {{$tablename}} t
  4169. ORDER BY t.name DESC";
  4170. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 4, 4));
  4171. $this->assertCount(2, $records);
  4172. $this->assertSame('b', reset($records)->name);
  4173. $this->assertSame('a', end($records)->name);
  4174. $sqlqm = "SELECT 1
  4175. FROM {{$tablename}} t
  4176. WHERE t.name = 'a'";
  4177. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 0, 1));
  4178. $this->assertCount(1, $records);
  4179. $sqlqm = "SELECT 'constant'
  4180. FROM {{$tablename}} t
  4181. WHERE t.name = 'a'";
  4182. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 0, 8));
  4183. $this->assertCount(1, $records);
  4184. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'a', 'content'=>'one')));
  4185. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'b', 'content'=>'two')));
  4186. $this->assertNotEmpty($DB->insert_record($tablename, array('name' => 'c', 'content'=>'three')));
  4187. $sqlqm = "SELECT t.name, COUNT(DISTINCT t2.id) AS count, 'Test' AS teststring
  4188. FROM {{$tablename}} t
  4189. LEFT JOIN (
  4190. SELECT t.id, t.name
  4191. FROM {{$tablename}} t
  4192. ) t2 ON t2.name = t.name
  4193. GROUP BY t.name
  4194. ORDER BY t.name ASC";
  4195. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm));
  4196. $this->assertCount(6, $records); // a,b,c,d,e,f.
  4197. $this->assertEquals(2, reset($records)->count); // a has 2 records now.
  4198. $this->assertEquals(1, end($records)->count); // f has 1 record still.
  4199. $this->assertNotEmpty($records = $DB->get_records_sql($sqlqm, null, 0, 2));
  4200. $this->assertCount(2, $records);
  4201. $this->assertEquals(2, reset($records)->count);
  4202. $this->assertEquals(2, end($records)->count);
  4203. }
  4204. public function test_queries_counter() {
  4205. $DB = $this->tdb;
  4206. $dbman = $this->tdb->get_manager();
  4207. // Test database.
  4208. $table = $this->get_test_table();
  4209. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  4210. $table->add_field('fieldvalue', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  4211. $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  4212. $dbman->create_table($table);
  4213. $tablename = $table->getName();
  4214. // Initial counters values.
  4215. $initreads = $DB->perf_get_reads();
  4216. $initwrites = $DB->perf_get_writes();
  4217. $previousqueriestime = $DB->perf_get_queries_time();
  4218. // Selects counts as reads.
  4219. // The get_records_sql() method generates only 1 db query.
  4220. $whatever = $DB->get_records_sql("SELECT * FROM {{$tablename}}");
  4221. $this->assertEquals($initreads + 1, $DB->perf_get_reads());
  4222. // The get_records() method generates 2 queries the first time is called
  4223. // as it is fetching the table structure.
  4224. $whatever = $DB->get_records($tablename);
  4225. $this->assertEquals($initreads + 3, $DB->perf_get_reads());
  4226. $this->assertEquals($initwrites, $DB->perf_get_writes());
  4227. // The elapsed time is counted.
  4228. $lastqueriestime = $DB->perf_get_queries_time();
  4229. $this->assertGreaterThanOrEqual($previousqueriestime, $lastqueriestime);
  4230. $previousqueriestime = $lastqueriestime;
  4231. // Only 1 now, it already fetched the table columns.
  4232. $whatever = $DB->get_records($tablename);
  4233. $this->assertEquals($initreads + 4, $DB->perf_get_reads());
  4234. // And only 1 more from now.
  4235. $whatever = $DB->get_records($tablename);
  4236. $this->assertEquals($initreads + 5, $DB->perf_get_reads());
  4237. // Inserts counts as writes.
  4238. $rec1 = new stdClass();
  4239. $rec1->fieldvalue = 11;
  4240. $rec1->id = $DB->insert_record($tablename, $rec1);
  4241. $this->assertEquals($initwrites + 1, $DB->perf_get_writes());
  4242. $this->assertEquals($initreads + 5, $DB->perf_get_reads());
  4243. // The elapsed time is counted.
  4244. $lastqueriestime = $DB->perf_get_queries_time();
  4245. $this->assertGreaterThanOrEqual($previousqueriestime, $lastqueriestime);
  4246. $previousqueriestime = $lastqueriestime;
  4247. $rec2 = new stdClass();
  4248. $rec2->fieldvalue = 22;
  4249. $rec2->id = $DB->insert_record($tablename, $rec2);
  4250. $this->assertEquals($initwrites + 2, $DB->perf_get_writes());
  4251. // Updates counts as writes.
  4252. $rec1->fieldvalue = 111;
  4253. $DB->update_record($tablename, $rec1);
  4254. $this->assertEquals($initwrites + 3, $DB->perf_get_writes());
  4255. $this->assertEquals($initreads + 5, $DB->perf_get_reads());
  4256. // The elapsed time is counted.
  4257. $lastqueriestime = $DB->perf_get_queries_time();
  4258. $this->assertGreaterThanOrEqual($previousqueriestime, $lastqueriestime);
  4259. $previousqueriestime = $lastqueriestime;
  4260. // Sum of them.
  4261. $totaldbqueries = $DB->perf_get_reads() + $DB->perf_get_writes();
  4262. $this->assertEquals($totaldbqueries, $DB->perf_get_queries());
  4263. }
  4264. }
  4265. /**
  4266. * This class is not a proper subclass of moodle_database. It is
  4267. * intended to be used only in unit tests, in order to gain access to the
  4268. * protected methods of moodle_database, and unit test them.
  4269. */
  4270. class moodle_database_for_testing extends moodle_database {
  4271. protected $prefix = 'mdl_';
  4272. public function public_fix_table_names($sql) {
  4273. return $this->fix_table_names($sql);
  4274. }
  4275. public function driver_installed() {}
  4276. public function get_dbfamily() {}
  4277. protected function get_dbtype() {}
  4278. protected function get_dblibrary() {}
  4279. public function get_name() {}
  4280. public function get_configuration_help() {}
  4281. public function connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, array $dboptions=null) {}
  4282. public function get_server_info() {}
  4283. protected function allowed_param_types() {}
  4284. public function get_last_error() {}
  4285. public function get_tables($usecache=true) {}
  4286. public function get_indexes($table) {}
  4287. public function get_columns($table, $usecache=true) {}
  4288. protected function normalise_value($column, $value) {}
  4289. public function set_debug($state) {}
  4290. public function get_debug() {}
  4291. public function set_logging($state) {}
  4292. public function change_database_structure($sql) {}
  4293. public function execute($sql, array $params=null) {}
  4294. public function get_recordset_sql($sql, array $params=null, $limitfrom=0, $limitnum=0) {}
  4295. public function get_records_sql($sql, array $params=null, $limitfrom=0, $limitnum=0) {}
  4296. public function get_fieldset_sql($sql, array $params=null) {}
  4297. public function insert_record_raw($table, $params, $returnid=true, $bulk=false, $customsequence=false) {}
  4298. public function insert_record($table, $dataobject, $returnid=true, $bulk=false) {}
  4299. public function import_record($table, $dataobject) {}
  4300. public function update_record_raw($table, $params, $bulk=false) {}
  4301. public function update_record($table, $dataobject, $bulk=false) {}
  4302. public function set_field_select($table, $newfield, $newvalue, $select, array $params=null) {}
  4303. public function delete_records_select($table, $select, array $params=null) {}
  4304. public function sql_concat() {}
  4305. public function sql_concat_join($separator="' '", $elements=array()) {}
  4306. public function sql_substr($expr, $start, $length=false) {}
  4307. public function begin_transaction() {}
  4308. public function commit_transaction() {}
  4309. public function rollback_transaction() {}
  4310. }
  4311. /**
  4312. * Dumb test class with toString() returning 1.
  4313. */
  4314. class dml_test_object_one {
  4315. public function __toString() {
  4316. return 1;
  4317. }
  4318. }