PageRenderTime 60ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/dmllib.php

https://bitbucket.org/ceu/moodle_demo
PHP | 2905 lines | 1378 code | 339 blank | 1188 comment | 415 complexity | 03aadfda35e4e1c88134f3388d93e371 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, LGPL-2.1
  1. <?php // $Id: dmllib.php,v 1.116.2.38 2011/12/01 12:25:16 moodlerobot Exp $
  2. ///////////////////////////////////////////////////////////////////////////
  3. // //
  4. // NOTICE OF COPYRIGHT //
  5. // //
  6. // Moodle - Modular Object-Oriented Dynamic Learning Environment //
  7. // http://moodle.com //
  8. // //
  9. // Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
  10. // //
  11. // This program is free software; you can redistribute it and/or modify //
  12. // it under the terms of the GNU General Public License as published by //
  13. // the Free Software Foundation; either version 2 of the License, or //
  14. // (at your option) any later version. //
  15. // //
  16. // This program is distributed in the hope that it will be useful, //
  17. // but WITHOUT ANY WARRANTY; without even the implied warranty of //
  18. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
  19. // GNU General Public License for more details: //
  20. // //
  21. // http://www.gnu.org/copyleft/gpl.html //
  22. // //
  23. ///////////////////////////////////////////////////////////////////////////
  24. /// This library contains all the Data Manipulation Language (DML) functions
  25. /// used to interact with the DB. All the dunctions in this library must be
  26. /// generic and work against the major number of RDBMS possible. This is the
  27. /// list of currently supported and tested DBs: mysql, postresql, mssql, oracle
  28. /// This library is automatically included by Moodle core so you never need to
  29. /// include it yourself.
  30. /// For more info about the functions available in this library, please visit:
  31. /// http://docs.moodle.org/19/en/DML_functions
  32. /// (feel free to modify, improve and document such page, thanks!)
  33. /// GLOBAL CONSTANTS /////////////////////////////////////////////////////////
  34. $empty_rs_cache = array(); // Keeps copies of the recordsets used in one invocation
  35. $metadata_cache = array(); // Kereeps copies of the MetaColumns() for each table used in one invocations
  36. $rcache = new StdClass; // Cache simple get_record results
  37. $rcache->data = array();
  38. $rcache->hits = 0;
  39. $rcache->misses = 0;
  40. /// FUNCTIONS FOR DATABASE HANDLING ////////////////////////////////
  41. /**
  42. * Execute a given sql command string
  43. *
  44. * Completely general function - it just runs some SQL and reports success.
  45. *
  46. * @uses $db
  47. * @param string $command The sql string you wish to be executed.
  48. * @param bool $feedback Set this argument to true if the results generated should be printed. Default is true.
  49. * @return bool success
  50. */
  51. function execute_sql($command, $feedback=true) {
  52. /// Completely general function - it just runs some SQL and reports success.
  53. global $db, $CFG;
  54. $olddebug = $db->debug;
  55. if (!$feedback) {
  56. $db->debug = false;
  57. }
  58. if ($CFG->version >= 2006101007) { //Look for trailing ; from Moodle 1.7.0
  59. $command = trim($command);
  60. /// If the trailing ; is there, fix and warn!
  61. if (substr($command, strlen($command)-1, 1) == ';') {
  62. /// One noticeable exception, Oracle PL/SQL blocks require ending in ";"
  63. if ($CFG->dbfamily == 'oracle' && substr($command, -4) == 'END;') {
  64. /// Nothing to fix/warn. The command is one PL/SQL block, so it's ok.
  65. } else {
  66. $command = trim($command, ';');
  67. debugging('Warning. Avoid to end your SQL commands with a trailing ";".', DEBUG_DEVELOPER);
  68. }
  69. }
  70. }
  71. $empty_rs_cache = array(); // Clear out the cache, just in case changes were made to table structures
  72. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  73. $rs = $db->Execute($command);
  74. $db->debug = $olddebug;
  75. if ($rs) {
  76. if ($feedback) {
  77. notify(get_string('success'), 'notifysuccess');
  78. }
  79. return true;
  80. } else {
  81. if ($feedback) {
  82. notify('<strong>' . get_string('error') . '</strong>');
  83. }
  84. // these two may go to difference places
  85. debugging($db->ErrorMsg() .'<br /><br />'. s($command));
  86. if (!empty($CFG->dblogerror)) {
  87. $debug=array_shift(debug_backtrace());
  88. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $command");
  89. }
  90. return false;
  91. }
  92. }
  93. /**
  94. * on DBs that support it, switch to transaction mode and begin a transaction
  95. * you'll need to ensure you call commit_sql() or your changes *will* be lost.
  96. *
  97. * Now using ADOdb standard transactions. Some day, we should switch to
  98. * Smart Transactions (http://phplens.com/adodb/tutorial.smart.transactions.html)
  99. * as they autodetect errors and are nestable and easier to write
  100. *
  101. * this is _very_ useful for massive updates
  102. */
  103. function begin_sql() {
  104. global $db;
  105. $db->BeginTrans();
  106. return true;
  107. }
  108. /**
  109. * on DBs that support it, commit the transaction
  110. *
  111. * Now using ADOdb standard transactions. Some day, we should switch to
  112. * Smart Transactions (http://phplens.com/adodb/tutorial.smart.transactions.html)
  113. * as they autodetect errors and are nestable and easier to write
  114. */
  115. function commit_sql() {
  116. global $db;
  117. $db->CommitTrans();
  118. return true;
  119. }
  120. /**
  121. * on DBs that support it, rollback the transaction
  122. *
  123. * Now using ADOdb standard transactions. Some day, we should switch to
  124. * Smart Transactions (http://phplens.com/adodb/tutorial.smart.transactions.html)
  125. * as they autodetect errors and are nestable and easier to write
  126. */
  127. function rollback_sql() {
  128. global $db;
  129. $db->RollbackTrans();
  130. return true;
  131. }
  132. /**
  133. * returns db specific uppercase function
  134. * @deprecated Moodle 1.7 because all the RDBMS use upper()
  135. */
  136. function db_uppercase() {
  137. return "upper";
  138. }
  139. /**
  140. * returns db specific lowercase function
  141. * @deprecated Moodle 1.7 because all the RDBMS use lower()
  142. */
  143. function db_lowercase() {
  144. return "lower";
  145. }
  146. /**
  147. * Run an arbitrary sequence of semicolon-delimited SQL commands
  148. *
  149. * Assumes that the input text (file or string) consists of
  150. * a number of SQL statements ENDING WITH SEMICOLONS. The
  151. * semicolons MUST be the last character in a line.
  152. * Lines that are blank or that start with "#" or "--" (postgres) are ignored.
  153. * Only tested with mysql dump files (mysqldump -p -d moodle)
  154. *
  155. * @uses $CFG
  156. *
  157. * @deprecated Moodle 1.7 use the new XMLDB stuff in lib/ddllib.php
  158. *
  159. * @param string $sqlfile The path where a file with sql commands can be found on the server.
  160. * @param string $sqlstring If no path is supplied then a string with semicolon delimited sql
  161. * commands can be supplied in this argument.
  162. * @return bool Returns true if databse was modified successfully.
  163. */
  164. function modify_database($sqlfile='', $sqlstring='') {
  165. global $CFG;
  166. if ($CFG->version > 2006101007) {
  167. debugging('Function modify_database() is deprecated. Replace it with the new XMLDB stuff.', DEBUG_DEVELOPER);
  168. }
  169. $success = true; // Let's be optimistic
  170. if (!empty($sqlfile)) {
  171. if (!is_readable($sqlfile)) {
  172. $success = false;
  173. echo '<p>Tried to modify database, but "'. $sqlfile .'" doesn\'t exist!</p>';
  174. return $success;
  175. } else {
  176. $lines = file($sqlfile);
  177. }
  178. } else {
  179. $sqlstring = trim($sqlstring);
  180. if ($sqlstring{strlen($sqlstring)-1} != ";") {
  181. $sqlstring .= ";"; // add it in if it's not there.
  182. }
  183. $lines[] = $sqlstring;
  184. }
  185. $command = '';
  186. foreach ($lines as $line) {
  187. $line = rtrim($line);
  188. $length = strlen($line);
  189. if ($length and $line[0] <> '#' and $line[0].$line[1] <> '--') {
  190. if (substr($line, $length-1, 1) == ';') {
  191. $line = substr($line, 0, $length-1); // strip ;
  192. $command .= $line;
  193. $command = str_replace('prefix_', $CFG->prefix, $command); // Table prefixes
  194. if (! execute_sql($command)) {
  195. $success = false;
  196. }
  197. $command = '';
  198. } else {
  199. $command .= $line;
  200. }
  201. }
  202. }
  203. return $success;
  204. }
  205. /// GENERIC FUNCTIONS TO CHECK AND COUNT RECORDS ////////////////////////////////////////
  206. /**
  207. * Test whether a record exists in a table where all the given fields match the given values.
  208. *
  209. * The record to test is specified by giving up to three fields that must
  210. * equal the corresponding values.
  211. *
  212. * @uses $CFG
  213. * @param string $table The table to check.
  214. * @param string $field1 the first field to check (optional).
  215. * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
  216. * @param string $field2 the second field to check (optional).
  217. * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
  218. * @param string $field3 the third field to check (optional).
  219. * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
  220. * @return bool true if a matching record exists, else false.
  221. */
  222. function record_exists($table, $field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
  223. global $CFG;
  224. $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
  225. return record_exists_sql('SELECT * FROM '. $CFG->prefix . $table .' '. $select);
  226. }
  227. /**
  228. * Test whether any records exists in a table which match a particular WHERE clause.
  229. *
  230. * @uses $CFG
  231. * @param string $table The database table to be checked against.
  232. * @param string $select A fragment of SQL to be used in a WHERE clause in the SQL call.
  233. * @return bool true if a matching record exists, else false.
  234. */
  235. function record_exists_select($table, $select='') {
  236. global $CFG;
  237. if ($select) {
  238. $select = 'WHERE '.$select;
  239. }
  240. return record_exists_sql('SELECT * FROM '. $CFG->prefix . $table . ' ' . $select);
  241. }
  242. /**
  243. * Test whether a SQL SELECT statement returns any records.
  244. *
  245. * This function returns true if the SQL statement executes
  246. * without any errors and returns at least one record.
  247. *
  248. * @param string $sql The SQL statement to execute.
  249. * @return bool true if the SQL executes without errors and returns at least one record.
  250. */
  251. function record_exists_sql($sql) {
  252. $limitfrom = 0; /// Number of records to skip
  253. $limitnum = 1; /// Number of records to retrieve
  254. if (!$rs = get_recordset_sql($sql, $limitfrom, $limitnum)) {
  255. return false;
  256. }
  257. if (rs_EOF($rs)) {
  258. $result = false;
  259. } else {
  260. $result = true;
  261. }
  262. rs_close($rs);
  263. return $result;
  264. }
  265. /**
  266. * Count the records in a table where all the given fields match the given values.
  267. *
  268. * @uses $CFG
  269. * @param string $table The table to query.
  270. * @param string $field1 the first field to check (optional).
  271. * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
  272. * @param string $field2 the second field to check (optional).
  273. * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
  274. * @param string $field3 the third field to check (optional).
  275. * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
  276. * @return int The count of records returned from the specified criteria.
  277. */
  278. function count_records($table, $field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
  279. global $CFG;
  280. $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
  281. return count_records_sql('SELECT COUNT(*) FROM '. $CFG->prefix . $table .' '. $select);
  282. }
  283. /**
  284. * Count the records in a table which match a particular WHERE clause.
  285. *
  286. * @uses $CFG
  287. * @param string $table The database table to be checked against.
  288. * @param string $select A fragment of SQL to be used in a WHERE clause in the SQL call.
  289. * @param string $countitem The count string to be used in the SQL call. Default is COUNT(*).
  290. * @return int The count of records returned from the specified criteria.
  291. */
  292. function count_records_select($table, $select='', $countitem='COUNT(*)') {
  293. global $CFG;
  294. if ($select) {
  295. $select = 'WHERE '.$select;
  296. }
  297. return count_records_sql('SELECT '. $countitem .' FROM '. $CFG->prefix . $table .' '. $select);
  298. }
  299. /**
  300. * Get the result of a SQL SELECT COUNT(...) query.
  301. *
  302. * Given a query that counts rows, return that count. (In fact,
  303. * given any query, return the first field of the first record
  304. * returned. However, this method should only be used for the
  305. * intended purpose.) If an error occurrs, 0 is returned.
  306. *
  307. * @uses $CFG
  308. * @uses $db
  309. * @param string $sql The SQL string you wish to be executed.
  310. * @return int the count. If an error occurrs, 0 is returned.
  311. */
  312. function count_records_sql($sql) {
  313. $rs = get_recordset_sql($sql);
  314. if (is_object($rs) and is_array($rs->fields)) {
  315. return reset($rs->fields);
  316. } else {
  317. return 0;
  318. }
  319. }
  320. /// GENERIC FUNCTIONS TO GET, INSERT, OR UPDATE DATA ///////////////////////////////////
  321. /**
  322. * Get a single record as an object
  323. *
  324. * @uses $CFG
  325. * @param string $table The table to select from.
  326. * @param string $field1 the first field to check (optional).
  327. * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
  328. * @param string $field2 the second field to check (optional).
  329. * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
  330. * @param string $field3 the third field to check (optional).
  331. * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
  332. * @return mixed a fieldset object containing the first mathcing record, or false if none found.
  333. */
  334. function get_record($table, $field1, $value1, $field2='', $value2='', $field3='', $value3='', $fields='*') {
  335. global $CFG;
  336. // Check to see whether this record is eligible for caching (fields=*, only condition is id)
  337. $docache = false;
  338. if (!empty($CFG->rcache) && $CFG->rcache === true && $field1=='id' && !$field2 && !$field3 && $fields=='*') {
  339. $docache = true;
  340. // If it's in the cache, return it
  341. $cached = rcache_getforfill($table, $value1);
  342. if (!empty($cached)) {
  343. return $cached;
  344. }
  345. }
  346. $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
  347. $record = get_record_sql('SELECT '.$fields.' FROM '. $CFG->prefix . $table .' '. $select);
  348. // If we're caching records, store this one
  349. // (supposing we got something - we don't cache failures)
  350. if ($docache) {
  351. if ($record !== false) {
  352. rcache_set($table, $value1, $record);
  353. } else {
  354. rcache_releaseforfill($table, $value1);
  355. }
  356. }
  357. return $record;
  358. }
  359. /**
  360. * Get a single record as an object using an SQL statement
  361. *
  362. * The SQL statement should normally only return one record. In debug mode
  363. * you will get a warning if more record is returned (unless you
  364. * set $expectmultiple to true). In non-debug mode, it just returns
  365. * the first record.
  366. *
  367. * @uses $CFG
  368. * @uses $db
  369. * @param string $sql The SQL string you wish to be executed, should normally only return one record.
  370. * @param bool $expectmultiple If the SQL cannot be written to conveniently return just one record,
  371. * set this to true to hide the debug message.
  372. * @param bool $nolimit sometimes appending ' LIMIT 1' to the SQL causes an error. Set this to true
  373. * to stop your SQL being modified. This argument should probably be deprecated.
  374. * @return Found record as object. False if not found or error
  375. */
  376. function get_record_sql($sql, $expectmultiple=false, $nolimit=false) {
  377. global $CFG;
  378. /// Default situation
  379. $limitfrom = 0; /// Number of records to skip
  380. $limitnum = 1; /// Number of records to retrieve
  381. /// Only a few uses of the 2nd and 3rd parameter have been found
  382. /// I think that we should avoid to use them completely, one
  383. /// record is one record, and everything else should return error.
  384. /// So the proposal is to change all the uses, (4-5 inside Moodle
  385. /// Core), drop them from the definition and delete the next two
  386. /// "if" sentences. (eloy, 2006-08-19)
  387. if ($nolimit) {
  388. $limitfrom = 0;
  389. $limitnum = 0;
  390. } else if ($expectmultiple) {
  391. $limitfrom = 0;
  392. $limitnum = 1;
  393. } else if (debugging('', DEBUG_DEVELOPER)) {
  394. // Debugging mode - don't use a limit of 1, but do change the SQL, because sometimes that
  395. // causes errors, and in non-debug mode you don't see the error message and it is
  396. // impossible to know what's wrong.
  397. $limitfrom = 0;
  398. $limitnum = 100;
  399. }
  400. if (!$rs = get_recordset_sql($sql, $limitfrom, $limitnum)) {
  401. return false;
  402. }
  403. $recordcount = $rs->RecordCount();
  404. if ($recordcount == 0) { // Found no records
  405. return false;
  406. } else if ($recordcount == 1) { // Found one record
  407. /// DIRTY HACK to retrieve all the ' ' (1 space) fields converted back
  408. /// to '' (empty string) for Oracle. It's the only way to work with
  409. /// all those NOT NULL DEFAULT '' fields until we definitively delete them
  410. if ($CFG->dbfamily == 'oracle') {
  411. array_walk($rs->fields, 'onespace2empty');
  412. }
  413. /// End of DIRTY HACK
  414. return (object)$rs->fields;
  415. } else { // Error: found more than one record
  416. notify('Error: Turn off debugging to hide this error.');
  417. notify($sql . '(with limits ' . $limitfrom . ', ' . $limitnum . ')');
  418. if ($records = $rs->GetAssoc(true)) {
  419. notify('Found more than one record in get_record_sql !');
  420. print_object($records);
  421. } else {
  422. notify('Very strange error in get_record_sql !');
  423. print_object($rs);
  424. }
  425. print_continue("$CFG->wwwroot/$CFG->admin/config.php");
  426. }
  427. }
  428. /**
  429. * Gets one record from a table, as an object
  430. *
  431. * @uses $CFG
  432. * @param string $table The database table to be checked against.
  433. * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
  434. * @param string $fields A comma separated list of fields to be returned from the chosen table.
  435. * @return object|false Returns an array of found records (as objects) or false if no records or error occured.
  436. */
  437. function get_record_select($table, $select='', $fields='*') {
  438. global $CFG;
  439. if ($select) {
  440. $select = 'WHERE '. $select;
  441. }
  442. return get_record_sql('SELECT '. $fields .' FROM '. $CFG->prefix . $table .' '. $select);
  443. }
  444. /**
  445. * Get a number of records as an ADODB RecordSet.
  446. *
  447. * Selects records from the table $table.
  448. *
  449. * If specified, only records where the field $field has value $value are retured.
  450. *
  451. * If specified, the results will be sorted as specified by $sort. This
  452. * is added to the SQL as "ORDER BY $sort". Example values of $sort
  453. * mightbe "time ASC" or "time DESC".
  454. *
  455. * If $fields is specified, only those fields are returned.
  456. *
  457. * Since this method is a little less readable, use of it should be restricted to
  458. * code where it's possible there might be large datasets being returned. For known
  459. * small datasets use get_records - it leads to simpler code.
  460. *
  461. * If you only want some of the records, specify $limitfrom and $limitnum.
  462. * The query will skip the first $limitfrom records (according to the sort
  463. * order) and then return the next $limitnum records. If either of $limitfrom
  464. * or $limitnum is specified, both must be present.
  465. *
  466. * The return value is an ADODB RecordSet object
  467. * @link http://phplens.com/adodb/reference.functions.adorecordset.html
  468. * if the query succeeds. If an error occurrs, false is returned.
  469. *
  470. * @param string $table the table to query.
  471. * @param string $field a field to check (optional).
  472. * @param string $value the value the field must have (requred if field1 is given, else optional).
  473. * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
  474. * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
  475. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  476. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  477. * @return mixed an ADODB RecordSet object, or false if an error occured.
  478. */
  479. function get_recordset($table, $field='', $value='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  480. if ($field) {
  481. $value = sql_magic_quotes_hack($value);
  482. $select = "$field = '$value'";
  483. } else {
  484. $select = '';
  485. }
  486. return get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
  487. }
  488. /**
  489. * Get a number of records as an ADODB RecordSet.
  490. *
  491. * If given, $select is used as the SELECT parameter in the SQL query,
  492. * otherwise all records from the table are returned.
  493. *
  494. * Other arguments and the return type as for @see function get_recordset.
  495. *
  496. * @uses $CFG
  497. * @param string $table the table to query.
  498. * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
  499. * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
  500. * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
  501. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  502. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  503. * @return mixed an ADODB RecordSet object, or false if an error occured.
  504. */
  505. function get_recordset_select($table, $select='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  506. global $CFG;
  507. if ($select) {
  508. $select = ' WHERE '. $select;
  509. }
  510. if ($sort) {
  511. $sort = ' ORDER BY '. $sort;
  512. }
  513. return get_recordset_sql('SELECT '. $fields .' FROM '. $CFG->prefix . $table . $select . $sort, $limitfrom, $limitnum);
  514. }
  515. /**
  516. * Get a number of records as an ADODB RecordSet.
  517. *
  518. * Only records where $field takes one of the values $values are returned.
  519. * $values should be a comma-separated list of values, for example "4,5,6,10"
  520. * or "'foo','bar','baz'".
  521. *
  522. * Other arguments and the return type as for @see function get_recordset.
  523. *
  524. * @param string $table the table to query.
  525. * @param string $field a field to check (optional).
  526. * @param string $values comma separated list of values the field must have (requred if field is given, else optional).
  527. * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
  528. * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
  529. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  530. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  531. * @return mixed an ADODB RecordSet object, or false if an error occured.
  532. */
  533. function get_recordset_list($table, $field='', $values='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  534. if ($field) {
  535. $select = "$field IN ($values)";
  536. } else {
  537. $select = '';
  538. }
  539. return get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
  540. }
  541. /**
  542. * Get a number of records as an ADODB RecordSet. $sql must be a complete SQL query.
  543. * Since this method is a little less readable, use of it should be restricted to
  544. * code where it's possible there might be large datasets being returned. For known
  545. * small datasets use get_records_sql - it leads to simpler code.
  546. *
  547. * The return type is as for @see function get_recordset.
  548. *
  549. * @uses $CFG
  550. * @uses $db
  551. * @param string $sql the SQL select query to execute.
  552. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  553. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  554. * @return mixed an ADODB RecordSet object, or false if an error occured.
  555. */
  556. function get_recordset_sql($sql, $limitfrom=null, $limitnum=null) {
  557. global $CFG, $db;
  558. if (empty($db)) {
  559. return false;
  560. }
  561. /// Temporary hack as part of phasing out all access to obsolete user tables XXX
  562. if (!empty($CFG->rolesactive)) {
  563. if (strpos($sql, ' '.$CFG->prefix.'user_students ') ||
  564. strpos($sql, ' '.$CFG->prefix.'user_teachers ') ||
  565. strpos($sql, ' '.$CFG->prefix.'user_coursecreators ') ||
  566. strpos($sql, ' '.$CFG->prefix.'user_admins ')) {
  567. if (debugging()) { var_dump(debug_backtrace()); }
  568. error('This SQL relies on obsolete tables! Your code must be fixed by a developer.');
  569. }
  570. }
  571. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  572. if ($limitfrom || $limitnum) {
  573. ///Special case, 0 must be -1 for ADOdb
  574. $limitfrom = empty($limitfrom) ? -1 : $limitfrom;
  575. $limitnum = empty($limitnum) ? -1 : $limitnum;
  576. $rs = $db->SelectLimit($sql, $limitnum, $limitfrom);
  577. } else {
  578. $rs = $db->Execute($sql);
  579. }
  580. if (!$rs) {
  581. debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
  582. if (!empty($CFG->dblogerror)) {
  583. $debug=array_shift(debug_backtrace());
  584. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql with limits ($limitfrom, $limitnum)");
  585. }
  586. return false;
  587. }
  588. return $rs;
  589. }
  590. /**
  591. * Utility function used by the following 4 methods. Note that for this to work, the first column
  592. * in the recordset must contain unique values, as it is used as the key to the associative array.
  593. *
  594. * @param object an ADODB RecordSet object.
  595. * @return mixed mixed an array of objects, or false if an error occured or the RecordSet was empty.
  596. */
  597. function recordset_to_array($rs) {
  598. global $CFG;
  599. $debugging = debugging('', DEBUG_DEVELOPER);
  600. if ($rs && !rs_EOF($rs)) {
  601. $objects = array();
  602. /// First of all, we are going to get the name of the first column
  603. /// to introduce it back after transforming the recordset to assoc array
  604. /// See http://docs.moodle.org/19/en/XMLDB_Problems, fetch mode problem.
  605. $firstcolumn = $rs->FetchField(0);
  606. /// Get the whole associative array
  607. if ($records = $rs->GetAssoc(true)) {
  608. foreach ($records as $key => $record) {
  609. /// Really DIRTY HACK for Oracle, but it's the only way to make it work
  610. /// until we got all those NOT NULL DEFAULT '' out from Moodle
  611. if ($CFG->dbfamily == 'oracle') {
  612. array_walk($record, 'onespace2empty');
  613. }
  614. /// End of DIRTY HACK
  615. $record[$firstcolumn->name] = $key;/// Re-add the assoc field
  616. if ($debugging && array_key_exists($key, $objects)) {
  617. debugging("Did you remember to make the first column something unique in your call to get_records? Duplicate value '$key' found in column '".$firstcolumn->name."'.", DEBUG_DEVELOPER);
  618. }
  619. $objects[$key] = (object) $record; /// To object
  620. }
  621. return $objects;
  622. /// Fallback in case we only have 1 field in the recordset. MDL-5877
  623. } else if ($rs->_numOfFields == 1 && $records = $rs->GetRows()) {
  624. foreach ($records as $key => $record) {
  625. /// Really DIRTY HACK for Oracle, but it's the only way to make it work
  626. /// until we got all those NOT NULL DEFAULT '' out from Moodle
  627. if ($CFG->dbfamily == 'oracle') {
  628. array_walk($record, 'onespace2empty');
  629. }
  630. /// End of DIRTY HACK
  631. if ($debugging && array_key_exists($record[$firstcolumn->name], $objects)) {
  632. debugging("Did you remember to make the first column something unique in your call to get_records? Duplicate value '".$record[$firstcolumn->name]."' found in column '".$firstcolumn->name."'.", DEBUG_DEVELOPER);
  633. }
  634. $objects[$record[$firstcolumn->name]] = (object) $record; /// The key is the first column value (like Assoc)
  635. }
  636. return $objects;
  637. } else {
  638. return false;
  639. }
  640. } else {
  641. return false;
  642. }
  643. }
  644. /**
  645. * This function is used to get the current record from the recordset. It
  646. * doesn't advance the recordset position. You'll need to do that by
  647. * using the rs_next_record($recordset) function.
  648. * @param ADORecordSet the recordset to fetch current record from
  649. * @return ADOFetchObj the object containing the fetched information
  650. */
  651. function rs_fetch_record(&$rs) {
  652. global $CFG;
  653. if (!$rs) {
  654. debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
  655. return false;
  656. }
  657. $rec = $rs->FetchObj(); //Retrieve record as object without advance the pointer
  658. if ($rs->EOF) { //FetchObj requires manual checking of EOF to detect if it's the last record
  659. $rec = false;
  660. } else {
  661. /// DIRTY HACK to retrieve all the ' ' (1 space) fields converted back
  662. /// to '' (empty string) for Oracle. It's the only way to work with
  663. /// all those NOT NULL DEFAULT '' fields until we definetively delete them
  664. if ($CFG->dbfamily == 'oracle') {
  665. $recarr = (array)$rec; /// Cast to array
  666. array_walk($recarr, 'onespace2empty');
  667. $rec = (object)$recarr;/// Cast back to object
  668. }
  669. /// End DIRTY HACK
  670. }
  671. return $rec;
  672. }
  673. /**
  674. * This function is used to advance the pointer of the recordset
  675. * to its next position/record.
  676. * @param ADORecordSet the recordset to be moved to the next record
  677. * @return boolean true if the movement was successful and false if not (end of recordset)
  678. */
  679. function rs_next_record(&$rs) {
  680. if (!$rs) {
  681. debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
  682. return false;
  683. }
  684. return $rs->MoveNext(); //Move the pointer to the next record
  685. }
  686. /**
  687. * This function is used to get the current record from the recordset. It
  688. * does advance the recordset position.
  689. * This is the prefered way to iterate over recordsets with code blocks like this:
  690. *
  691. * $rs = get_recordset('SELECT .....');
  692. * while ($rec = rs_fetch_next_record($rs)) {
  693. * /// Perform actions with the $rec record here
  694. * }
  695. * rs_close($rs); /// Close the recordset if not used anymore. Saves memory (optional but recommended).
  696. *
  697. * @param ADORecordSet the recordset to fetch current record from
  698. * @return mixed ADOFetchObj the object containing the fetched information or boolean false if no record (end of recordset)
  699. */
  700. function rs_fetch_next_record(&$rs) {
  701. global $CFG;
  702. if (!$rs) {
  703. debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
  704. return false;
  705. }
  706. $rec = false;
  707. $recarr = $rs->FetchRow(); //Retrieve record as object without advance the pointer. It's quicker that FetchNextObj()
  708. if ($recarr) {
  709. /// DIRTY HACK to retrieve all the ' ' (1 space) fields converted back
  710. /// to '' (empty string) for Oracle. It's the only way to work with
  711. /// all those NOT NULL DEFAULT '' fields until we definetively delete them
  712. if ($CFG->dbfamily == 'oracle') {
  713. array_walk($recarr, 'onespace2empty');
  714. }
  715. /// End DIRTY HACK
  716. /// Cast array to object
  717. $rec = (object)$recarr;
  718. }
  719. return $rec;
  720. }
  721. /**
  722. * Returns true if no more records found
  723. * @param ADORecordSet the recordset
  724. * @return bool
  725. */
  726. function rs_EOF($rs) {
  727. if (!$rs) {
  728. debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
  729. return true;
  730. }
  731. return $rs->EOF;
  732. }
  733. /**
  734. * This function closes the recordset, freeing all the memory and associated resources.
  735. * Note that, once closed, the recordset must not be used anymore along the request.
  736. * Saves memory (optional but recommended).
  737. * @param ADORecordSet the recordset to be closed
  738. * @return void
  739. */
  740. function rs_close(&$rs) {
  741. if (!$rs) {
  742. debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
  743. return;
  744. }
  745. $rs->Close();
  746. }
  747. /**
  748. * This function is used to convert all the Oracle 1-space defaults to the empty string
  749. * like a really DIRTY HACK to allow it to work better until all those NOT NULL DEFAULT ''
  750. * fields will be out from Moodle.
  751. * @param string the string to be converted to '' (empty string) if it's ' ' (one space)
  752. * @param mixed the key of the array in case we are using this function from array_walk,
  753. * defaults to null for other (direct) uses
  754. * @return boolean always true (the converted variable is returned by reference)
  755. */
  756. function onespace2empty(&$item, $key=null) {
  757. $item = $item == ' ' ? '' : $item;
  758. return true;
  759. }
  760. ///End DIRTY HACK
  761. /**
  762. * Get a number of records as an array of objects.
  763. *
  764. * If the query succeeds and returns at least one record, the
  765. * return value is an array of objects, one object for each
  766. * record found. The array key is the value from the first
  767. * column of the result set. The object associated with that key
  768. * has a member variable for each column of the results.
  769. *
  770. * @param string $table the table to query.
  771. * @param string $field a field to check (optional).
  772. * @param string $value the value the field must have (requred if field1 is given, else optional).
  773. * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
  774. * @param string $fields a comma separated list of fields to return (optional, by default
  775. * all fields are returned). The first field will be used as key for the
  776. * array so must be a unique field such as 'id'.
  777. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  778. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  779. * @return mixed an array of objects, or false if no records were found or an error occured.
  780. */
  781. function get_records($table, $field='', $value='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  782. $rs = get_recordset($table, $field, $value, $sort, $fields, $limitfrom, $limitnum);
  783. return recordset_to_array($rs);
  784. }
  785. /**
  786. * Get a number of records as an array of objects.
  787. *
  788. * Return value as for @see function get_records.
  789. *
  790. * @param string $table the table to query.
  791. * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
  792. * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
  793. * @param string $fields a comma separated list of fields to return
  794. * (optional, by default all fields are returned). The first field will be used as key for the
  795. * array so must be a unique field such as 'id'.
  796. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  797. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  798. * @return mixed an array of objects, or false if no records were found or an error occured.
  799. */
  800. function get_records_select($table, $select='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  801. $rs = get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
  802. return recordset_to_array($rs);
  803. }
  804. /**
  805. * Get a number of records as an array of objects.
  806. *
  807. * Return value as for @see function get_records.
  808. *
  809. * @param string $table The database table to be checked against.
  810. * @param string $field The field to search
  811. * @param string $values Comma separated list of possible value
  812. * @param string $sort Sort order (as valid SQL sort parameter)
  813. * @param string $fields A comma separated list of fields to be returned from the chosen table. If specified,
  814. * the first field should be a unique one such as 'id' since it will be used as a key in the associative
  815. * array.
  816. * @return mixed an array of objects, or false if no records were found or an error occured.
  817. */
  818. function get_records_list($table, $field='', $values='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  819. $rs = get_recordset_list($table, $field, $values, $sort, $fields, $limitfrom, $limitnum);
  820. return recordset_to_array($rs);
  821. }
  822. /**
  823. * Get a number of records as an array of objects.
  824. *
  825. * Return value as for @see function get_records.
  826. *
  827. * @param string $sql the SQL select query to execute. The first column of this SELECT statement
  828. * must be a unique value (usually the 'id' field), as it will be used as the key of the
  829. * returned array.
  830. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  831. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  832. * @return mixed an array of objects, or false if no records were found or an error occured.
  833. */
  834. function get_records_sql($sql, $limitfrom='', $limitnum='') {
  835. $rs = get_recordset_sql($sql, $limitfrom, $limitnum);
  836. return recordset_to_array($rs);
  837. }
  838. /**
  839. * Utility function used by the following 3 methods.
  840. *
  841. * @param object an ADODB RecordSet object with two columns.
  842. * @return mixed an associative array, or false if an error occured or the RecordSet was empty.
  843. */
  844. function recordset_to_menu($rs) {
  845. global $CFG;
  846. $menu = array();
  847. if ($rs && !rs_EOF($rs)) {
  848. $keys = array_keys($rs->fields);
  849. $key0=$keys[0];
  850. $key1=$keys[1];
  851. while (!$rs->EOF) {
  852. $menu[$rs->fields[$key0]] = $rs->fields[$key1];
  853. $rs->MoveNext();
  854. }
  855. /// Really DIRTY HACK for Oracle, but it's the only way to make it work
  856. /// until we got all those NOT NULL DEFAULT '' out from Moodle
  857. if ($CFG->dbfamily == 'oracle') {
  858. array_walk($menu, 'onespace2empty');
  859. }
  860. /// End of DIRTY HACK
  861. return $menu;
  862. } else {
  863. return false;
  864. }
  865. }
  866. /**
  867. * Utility function
  868. * Similar to recordset_to_menu
  869. *
  870. * field1, field2 is needed because the order from get_records_sql is not reliable
  871. * @param records - records from get_records_sql() or get_records()
  872. * @param field1 - field to be used as menu index
  873. * @param field2 - feild to be used as coresponding menu value
  874. * @return mixed an associative array, or false if an error occured or the RecordSet was empty.
  875. */
  876. function records_to_menu($records, $field1, $field2) {
  877. $menu = array();
  878. foreach ($records as $record) {
  879. $menu[$record->$field1] = $record->$field2;
  880. }
  881. if (!empty($menu)) {
  882. return $menu;
  883. } else {
  884. return false;
  885. }
  886. }
  887. /**
  888. * Get the first two columns from a number of records as an associative array.
  889. *
  890. * Arguments as for @see function get_recordset.
  891. *
  892. * If no errors occur, and at least one records is found, the return value
  893. * is an associative whose keys come from the first field of each record,
  894. * and whose values are the corresponding second fields. If no records are found,
  895. * or an error occurs, false is returned.
  896. *
  897. * @param string $table the table to query.
  898. * @param string $field a field to check (optional).
  899. * @param string $value the value the field must have (requred if field1 is given, else optional).
  900. * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
  901. * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
  902. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  903. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  904. * @return mixed an associative array, or false if no records were found or an error occured.
  905. */
  906. function get_records_menu($table, $field='', $value='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  907. $rs = get_recordset($table, $field, $value, $sort, $fields, $limitfrom, $limitnum);
  908. return recordset_to_menu($rs);
  909. }
  910. /**
  911. * Get the first two columns from a number of records as an associative array.
  912. *
  913. * Arguments as for @see function get_recordset_select.
  914. * Return value as for @see function get_records_menu.
  915. *
  916. * @param string $table The database table to be checked against.
  917. * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
  918. * @param string $sort Sort order (optional) - a valid SQL order parameter
  919. * @param string $fields A comma separated list of fields to be returned from the chosen table.
  920. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  921. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  922. * @return mixed an associative array, or false if no records were found or an error occured.
  923. */
  924. function get_records_select_menu($table, $select='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  925. $rs = get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
  926. return recordset_to_menu($rs);
  927. }
  928. /**
  929. * Get the first two columns from a number of records as an associative array.
  930. *
  931. * Arguments as for @see function get_recordset_sql.
  932. * Return value as for @see function get_records_menu.
  933. *
  934. * @param string $sql The SQL string you wish to be executed.
  935. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  936. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  937. * @return mixed an associative array, or false if no records were found or an error occured.
  938. */
  939. function get_records_sql_menu($sql, $limitfrom='', $limitnum='') {
  940. $rs = get_recordset_sql($sql, $limitfrom, $limitnum);
  941. return recordset_to_menu($rs);
  942. }
  943. /**
  944. * Get a single value from a table row where all the given fields match the given values.
  945. *
  946. * @param string $table the table to query.
  947. * @param string $return the field to return the value of.
  948. * @param string $field1 the first field to check (optional).
  949. * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
  950. * @param string $field2 the second field to check (optional).
  951. * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
  952. * @param string $field3 the third field to check (optional).
  953. * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
  954. * @return mixed the specified value, or false if an error occured.
  955. */
  956. function get_field($table, $return, $field1, $value1, $field2='', $value2='', $field3='', $value3='') {
  957. global $CFG;
  958. $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
  959. return get_field_sql('SELECT ' . $return . ' FROM ' . $CFG->prefix . $table . ' ' . $select);
  960. }
  961. /**
  962. * Get a single value from a table row where a particular select clause is true.
  963. *
  964. * @uses $CFG
  965. * @param string $table the table to query.
  966. * @param string $return the field to return the value of.
  967. * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
  968. * @return mixed the specified value, or false if an error occured.
  969. */
  970. function get_field_select($table, $return, $select) {
  971. global $CFG;
  972. if ($select) {
  973. $select = 'WHERE '. $select;
  974. }
  975. return get_field_sql('SELECT ' . $return . ' FROM ' . $CFG->prefix . $table . ' ' . $select);
  976. }
  977. /**
  978. * Get a single value from a table.
  979. *
  980. * @param string $sql an SQL statement expected to return a single value.
  981. * @return mixed the specified value, or false if an error occured.
  982. */
  983. function get_field_sql($sql) {
  984. global $CFG;
  985. /// Strip potential LIMIT uses arriving here, debugging them (MDL-7173)
  986. $newsql = preg_replace('/ LIMIT [0-9, ]+$/is', '', $sql);
  987. if ($newsql != $sql) {
  988. debugging('Incorrect use of LIMIT clause (not cross-db) in call to get_field_sql(): ' . s($sql), DEBUG_DEVELOPER);
  989. $sql = $newsql;
  990. }
  991. $rs = get_recordset_sql($sql, 0, 1);
  992. if ($rs && $rs->RecordCount() == 1) {
  993. /// DIRTY HACK to retrieve all the ' ' (1 space) fields converted back
  994. /// to '' (empty string) for Oracle. It's the only way to work with
  995. /// all those NOT NULL DEFAULT '' fields until we definetively delete them
  996. if ($CFG->dbfamily == 'oracle') {
  997. $value = reset($rs->fields);
  998. onespace2empty($value);
  999. return $value;
  1000. }
  1001. /// End of DIRTY HACK
  1002. return reset($rs->fields);
  1003. } else {
  1004. return false;
  1005. }
  1006. }
  1007. /**
  1008. * Get a single value from a table row where a particular select clause is true.
  1009. *
  1010. * @uses $CFG
  1011. * @param string $table the table to query.
  1012. * @param string $return the field to return the value of.
  1013. * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
  1014. * @return mixed|false Returns the value return from the SQL statment or false if an error occured.
  1015. */
  1016. function get_fieldset_select($table, $return, $select) {
  1017. global $CFG;
  1018. if ($select) {
  1019. $select = ' WHERE '. $select;
  1020. }
  1021. return get_fieldset_sql('SELECT ' . $return . ' FROM ' . $CFG->prefix . $table . $select);
  1022. }
  1023. /**
  1024. * Get an array of data from one or more fields from a database
  1025. * use to get a column, or a series of distinct values
  1026. *
  1027. * @uses $CFG
  1028. * @uses $db
  1029. * @param string $sql The SQL string you wish to be executed.
  1030. * @return mixed|false Returns the value return from the SQL statment or false if an error occured.
  1031. * @todo Finish documenting this function
  1032. */
  1033. function get_fieldset_sql($sql) {
  1034. global $db, $CFG;
  1035. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  1036. $rs = $db->Execute($sql);
  1037. if (!$rs) {
  1038. debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
  1039. if (!empty($CFG->dblogerror)) {
  1040. $debug=array_shift(debug_backtrace());
  1041. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
  1042. }
  1043. return false;
  1044. }
  1045. if ( !rs_EOF($rs) ) {
  1046. $keys = array_keys($rs->fields);
  1047. $key0 = $keys[0];
  1048. $results = array();
  1049. while (!$rs->EOF) {
  1050. array_push($results, $rs->fields[$key0]);
  1051. $rs->MoveNext();
  1052. }
  1053. /// DIRTY HACK to retrieve all the ' ' (1 space) fields converted back
  1054. /// to '' (empty string) for Oracle. It's the only way to work with
  1055. /// all those NOT NULL DEFAULT '' fields until we definetively delete them
  1056. if ($CFG->dbfamily == 'oracle') {
  1057. array_walk($results, 'onespace2empty');
  1058. }
  1059. /// End of DIRTY HACK
  1060. rs_close($rs);
  1061. return $results;
  1062. } else {
  1063. rs_close($rs);
  1064. return false;
  1065. }
  1066. }
  1067. /**
  1068. * Set a single field in every table row where all the given fields match the given values.
  1069. *
  1070. * @uses $CFG
  1071. * @uses $db
  1072. * @param string $table The database table to be checked against.
  1073. * @param string $newfield the field to set.
  1074. * @param string $newvalue the value to set the field to.
  1075. * @param string $field1 the first field to check (optional).
  1076. * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
  1077. * @param string $field2 the second field to check (optional).
  1078. * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
  1079. * @param string $field3 the third field to check (optional).
  1080. * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
  1081. * @return mixed An ADODB RecordSet object with the results from the SQL call or false.
  1082. */
  1083. function set_field($table, $newfield, $newvalue, $field1, $value1, $field2='', $value2='', $field3='', $value3='') {
  1084. global $CFG;
  1085. // Clear record_cache based on the parameters passed
  1086. // (individual record or whole table)
  1087. if ($CFG->rcache === true) {
  1088. if ($field1 == 'id') {
  1089. rcache_unset($table, $value1);
  1090. } else if ($field2 == 'id') {
  1091. rcache_unset($table, $value2);
  1092. } else if ($field3 == 'id') {
  1093. rcache_unset($table, $value3);
  1094. } else {
  1095. rcache_unset_table($table);
  1096. }
  1097. }
  1098. $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
  1099. return set_field_select($table, $newfield, $newvalue, $select, true);
  1100. }
  1101. /**
  1102. * Set a single field in every table row where the select statement evaluates to true.
  1103. *
  1104. * @uses $CFG
  1105. * @uses $db
  1106. * @param string $table The database table to be checked against.
  1107. * @param string $newfield the field to set.
  1108. * @param string $newvalue the value to set the field to.
  1109. * @param string $select a fragment of SQL to be used in a where clause in the SQL call.
  1110. * @param boolean $localcall Leave this set to false. (Should only be set to true by set_field.)
  1111. * @return mixed An ADODB RecordSet object with the results from the SQL call or false.
  1112. */
  1113. function set_field_select($table, $newfield, $newvalue, $select, $localcall = false) {
  1114. global $db, $CFG;
  1115. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  1116. if (!$localcall) {
  1117. if ($select) {
  1118. $select = 'WHERE ' . $select;
  1119. }
  1120. // Clear record_cache based on the parameters passed
  1121. // (individual record or whole table)
  1122. if ($CFG->rcache === true) {
  1123. rcache_unset_table($table);
  1124. }
  1125. }
  1126. $dataobject = new StdClass;
  1127. $dataobject->{$newfield} = $newvalue;
  1128. // Oracle DIRTY HACK -
  1129. if ($CFG->dbfamily == 'oracle') {
  1130. oracle_dirty_hack($table, $dataobject); // Convert object to the correct "empty" values for Oracle DB
  1131. $newvalue = $dataobject->{$newfield};
  1132. }
  1133. // End DIRTY HACK
  1134. /// Under Oracle, MSSQL and PostgreSQL we have our own set field process
  1135. /// If the field being updated is clob/blob, we use our alternate update here
  1136. /// They will be updated later
  1137. if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') && !empty($select)) {
  1138. /// Detect lobs
  1139. $foundclobs = array();
  1140. $foundblobs = array();
  1141. db_detect_lobs($table, $dataobject, $foundclobs, $foundblobs);
  1142. }
  1143. /// Under Oracle, MSSQL and PostgreSQL, finally, update all the Clobs and Blobs present in the record
  1144. /// if we know we have some of them in the query
  1145. if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') && !empty($select) &&
  1146. (!empty($foundclobs) || !empty($foundblobs))) {
  1147. if (!db_update_lobs($table, $select, $foundclobs, $foundblobs)) {
  1148. return false; //Some error happened while updating LOBs
  1149. } else {
  1150. return true; //Everrything was ok
  1151. }
  1152. }
  1153. /// NULL inserts - introduced in 1.9
  1154. if (is_null($newvalue)) {
  1155. $update = "$newfield = NULL";
  1156. } else {
  1157. $update = "$newfield = '$newvalue'";
  1158. }
  1159. /// Arriving here, standard update
  1160. $sql = 'UPDATE '. $CFG->prefix . $table .' SET '.$update.' '.$select;
  1161. $rs = $db->Execute($sql);
  1162. if (!$rs) {
  1163. debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
  1164. if (!empty($CFG->dblogerror)) {
  1165. $debug=array_shift(debug_backtrace());
  1166. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
  1167. }
  1168. return false;
  1169. }
  1170. return $rs;
  1171. }
  1172. /**
  1173. * Delete the records from a table where all the given fields match the given values.
  1174. *
  1175. * @uses $CFG
  1176. * @uses $db
  1177. * @param string $table the table to delete from.
  1178. * @param string $field1 the first field to check (optional).
  1179. * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
  1180. * @param string $field2 the second field to check (optional).
  1181. * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
  1182. * @param string $field3 the third field to check (optional).
  1183. * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
  1184. * @return mixed An ADODB RecordSet object with the results from the SQL call or false.
  1185. */
  1186. function delete_records($table, $field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
  1187. global $db, $CFG;
  1188. // Clear record_cache based on the parameters passed
  1189. // (individual record or whole table)
  1190. if ($CFG->rcache === true) {
  1191. if ($field1 == 'id') {
  1192. rcache_unset($table, $value1);
  1193. } else if ($field2 == 'id') {
  1194. rcache_unset($table, $value2);
  1195. } else if ($field3 == 'id') {
  1196. rcache_unset($table, $value3);
  1197. } else {
  1198. rcache_unset_table($table);
  1199. }
  1200. }
  1201. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  1202. $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
  1203. $sql = 'DELETE FROM '. $CFG->prefix . $table .' '. $select;
  1204. $rs = $db->Execute($sql);
  1205. if (!$rs) {
  1206. debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
  1207. if (!empty($CFG->dblogerror)) {
  1208. $debug=array_shift(debug_backtrace());
  1209. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
  1210. }
  1211. return false;
  1212. }
  1213. return $rs;
  1214. }
  1215. /**
  1216. * Delete one or more records from a table
  1217. *
  1218. * @uses $CFG
  1219. * @uses $db
  1220. * @param string $table The database table to be checked against.
  1221. * @param string $select A fragment of SQL to be used in a where clause in the SQL call (used to define the selection criteria).
  1222. * @return object A PHP standard object with the results from the SQL call.
  1223. * @todo Verify return type.
  1224. */
  1225. function delete_records_select($table, $select='') {
  1226. global $CFG, $db;
  1227. // Clear record_cache (whole table)
  1228. if ($CFG->rcache === true) {
  1229. rcache_unset_table($table);
  1230. }
  1231. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  1232. if ($select) {
  1233. $select = 'WHERE '.$select;
  1234. }
  1235. $sql = 'DELETE FROM '. $CFG->prefix . $table .' '. $select;
  1236. $rs = $db->Execute($sql);
  1237. if (!$rs) {
  1238. debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
  1239. if (!empty($CFG->dblogerror)) {
  1240. $debug=array_shift(debug_backtrace());
  1241. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
  1242. }
  1243. return false;
  1244. }
  1245. return $rs;
  1246. }
  1247. /**
  1248. * Insert a record into a table and return the "id" field if required
  1249. *
  1250. * If the return ID isn't required, then this just reports success as true/false.
  1251. * $dataobject is an object containing needed data
  1252. *
  1253. * @uses $db
  1254. * @uses $CFG
  1255. * @param string $table The database table to be checked against.
  1256. * @param object $dataobject A data object with values for one or more fields in the record
  1257. * @param bool $returnid Should the id of the newly created record entry be returned? If this option is not requested then true/false is returned.
  1258. * @param string $primarykey (obsolete) This is now forced to be 'id'.
  1259. */
  1260. function insert_record($table, $dataobject, $returnid=true, $primarykey='id') {
  1261. global $db, $CFG, $empty_rs_cache;
  1262. if (empty($db)) {
  1263. return false;
  1264. }
  1265. /// Check we are handling a proper $dataobject
  1266. if (is_array($dataobject)) {
  1267. debugging('Warning. Wrong call to insert_record(). $dataobject must be an object. array found instead', DEBUG_DEVELOPER);
  1268. $dataobject = (object)$dataobject;
  1269. } else if (is_object($dataobject)) {
  1270. // make sure there are no properties or private methods because we cast to array later,
  1271. // at the same time this undos the object references so that PHP 5 works the same as PHP 4,
  1272. // the main reason for this is BC after the dirty magic hack introduction
  1273. if ($properties = get_object_vars($dataobject)) {
  1274. $dataobject = (object)$properties;
  1275. }
  1276. unset($properties);
  1277. }
  1278. /// Temporary hack as part of phasing out all access to obsolete user tables XXX
  1279. if (!empty($CFG->rolesactive)) {
  1280. if (in_array($table, array('user_students', 'user_teachers', 'user_coursecreators', 'user_admins'))) {
  1281. if (debugging()) { var_dump(debug_backtrace()); }
  1282. error('This SQL relies on obsolete tables ('.$table.')! Your code must be fixed by a developer.');
  1283. }
  1284. }
  1285. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  1286. /// In Moodle we always use auto-numbering fields for the primary key
  1287. /// so let's unset it now before it causes any trouble later
  1288. unset($dataobject->{$primarykey});
  1289. /// Extra protection against SQL injections
  1290. foreach((array)$dataobject as $k=>$v) {
  1291. $dataobject->$k = sql_magic_quotes_hack($v);
  1292. }
  1293. /// Get an empty recordset. Cache for multiple inserts.
  1294. if (empty($empty_rs_cache[$table])) {
  1295. /// Execute a dummy query to get an empty recordset
  1296. if (!$empty_rs_cache[$table] = $db->Execute('SELECT * FROM '. $CFG->prefix . $table .' WHERE '. $primarykey .' = \'-1\'')) {
  1297. return false;
  1298. }
  1299. }
  1300. $rs = $empty_rs_cache[$table];
  1301. /// Postgres doesn't have the concept of primary key built in
  1302. /// and will return the OID which isn't what we want.
  1303. /// The efficient and transaction-safe strategy is to
  1304. /// move the sequence forward first, and make the insert
  1305. /// with an explicit id.
  1306. if ( $CFG->dbfamily === 'postgres' && $returnid == true ) {
  1307. if ($nextval = (int)get_field_sql("SELECT NEXTVAL('{$CFG->prefix}{$table}_{$primarykey}_seq')")) {
  1308. $dataobject->{$primarykey} = $nextval;
  1309. }
  1310. }
  1311. /// Begin DIRTY HACK
  1312. if ($CFG->dbfamily == 'oracle') {
  1313. oracle_dirty_hack($table, $dataobject); // Convert object to the correct "empty" values for Oracle DB
  1314. }
  1315. /// End DIRTY HACK
  1316. /// Under Oracle, MSSQL and PostgreSQL we have our own insert record process
  1317. /// detect all the clob/blob fields and change their contents to @#CLOB#@ and @#BLOB#@
  1318. /// saving them into $foundclobs and $foundblobs [$fieldname]->contents
  1319. /// Same for mssql (only processing blobs - image fields)
  1320. if ($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') {
  1321. $foundclobs = array();
  1322. $foundblobs = array();
  1323. db_detect_lobs($table, $dataobject, $foundclobs, $foundblobs);
  1324. }
  1325. /// Under Oracle, if the primary key inserted has been requested OR
  1326. /// if there are LOBs to insert, we calculate the next value via
  1327. /// explicit query to the sequence.
  1328. /// Else, the pre-insert trigger will do the job, because the primary
  1329. /// key isn't needed at all by the rest of PHP code
  1330. if ($CFG->dbfamily === 'oracle' && ($returnid == true || !empty($foundclobs) || !empty($foundblobs))) {
  1331. /// We need this here (move this function to dmlib?)
  1332. include_once($CFG->libdir . '/ddllib.php');
  1333. $xmldb_table = new XMLDBTable($table);
  1334. $seqname = find_sequence_name($xmldb_table);
  1335. if (!$seqname) {
  1336. /// Fallback, seqname not found, something is wrong. Inform and use the alternative getNameForObject() method
  1337. debugging('Sequence name for table ' . $xmldb_table->getName() . ' not found', DEBUG_DEVELOPER);
  1338. $generator = new XMLDBoci8po();
  1339. $generator->setPrefix($CFG->prefix);
  1340. $seqname = $generator->getNameForObject($table, $primarykey, 'seq');
  1341. }
  1342. if ($nextval = (int)$db->GenID($seqname)) {
  1343. $dataobject->{$primarykey} = $nextval;
  1344. } else {
  1345. debugging('Not able to get value from sequence ' . $seqname, DEBUG_DEVELOPER);
  1346. }
  1347. }
  1348. /// Get the correct SQL from adoDB
  1349. if (!$insertSQL = $db->GetInsertSQL($rs, (array)$dataobject, true)) {
  1350. return false;
  1351. }
  1352. /// Under Oracle, MSSQL and PostgreSQL, replace all the '@#CLOB#@' and '@#BLOB#@' ocurrences to proper default values
  1353. /// if we know we have some of them in the query
  1354. if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') &&
  1355. (!empty($foundclobs) || !empty($foundblobs))) {
  1356. /// Initial configuration, based on DB
  1357. switch ($CFG->dbfamily) {
  1358. case 'oracle':
  1359. $clobdefault = 'empty_clob()'; //Value of empty default clobs for this DB
  1360. $blobdefault = 'empty_blob()'; //Value of empty default blobs for this DB
  1361. break;
  1362. case 'mssql':
  1363. case 'postgres':
  1364. $clobdefault = 'null'; //Value of empty default clobs for this DB (under mssql this won't be executed
  1365. $blobdefault = 'null'; //Value of empty default blobs for this DB
  1366. break;
  1367. }
  1368. $insertSQL = str_replace("'@#CLOB#@'", $clobdefault, $insertSQL);
  1369. $insertSQL = str_replace("'@#BLOB#@'", $blobdefault, $insertSQL);
  1370. }
  1371. /// Run the SQL statement
  1372. if (!$rs = $db->Execute($insertSQL)) {
  1373. debugging($db->ErrorMsg() .'<br /><br />'.s($insertSQL));
  1374. if (!empty($CFG->dblogerror)) {
  1375. $debug=array_shift(debug_backtrace());
  1376. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $insertSQL");
  1377. }
  1378. return false;
  1379. }
  1380. /// Under Oracle and PostgreSQL, finally, update all the Clobs and Blobs present in the record
  1381. /// if we know we have some of them in the query
  1382. if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'postgres') &&
  1383. !empty($dataobject->{$primarykey}) &&
  1384. (!empty($foundclobs) || !empty($foundblobs))) {
  1385. if (!db_update_lobs($table, $dataobject->{$primarykey}, $foundclobs, $foundblobs)) {
  1386. return false; //Some error happened while updating LOBs
  1387. }
  1388. }
  1389. /// If a return ID is not needed then just return true now (but not in MSSQL DBs, where we may have some pending tasks)
  1390. if (!$returnid && $CFG->dbfamily != 'mssql') {
  1391. return true;
  1392. }
  1393. /// We already know the record PK if it's been passed explicitly,
  1394. /// or if we've retrieved it from a sequence (Postgres and Oracle).
  1395. if (!empty($dataobject->{$primarykey})) {
  1396. return $dataobject->{$primarykey};
  1397. }
  1398. /// This only gets triggered with MySQL and MSQL databases
  1399. /// however we have some postgres fallback in case we failed
  1400. /// to find the sequence.
  1401. $id = $db->Insert_ID();
  1402. /// Under MSSQL all the Clobs and Blobs (IMAGE) present in the record
  1403. /// if we know we have some of them in the query
  1404. if (($CFG->dbfamily == 'mssql') &&
  1405. !empty($id) &&
  1406. (!empty($foundclobs) || !empty($foundblobs))) {
  1407. if (!db_update_lobs($table, $id, $foundclobs, $foundblobs)) {
  1408. return false; //Some error happened while updating LOBs
  1409. }
  1410. }
  1411. if ($CFG->dbfamily === 'postgres') {
  1412. // try to get the primary key based on id
  1413. if ( ($rs = $db->Execute('SELECT '. $primarykey .' FROM '. $CFG->prefix . $table .' WHERE oid = '. $id))
  1414. && ($rs->RecordCount() == 1) ) {
  1415. trigger_error("Retrieved $primarykey from oid on table $table because we could not find the sequence.");
  1416. return (integer)reset($rs->fields);
  1417. }
  1418. trigger_error('Failed to retrieve primary key after insert: SELECT '. $primarykey .
  1419. ' FROM '. $CFG->prefix . $table .' WHERE oid = '. $id);
  1420. return false;
  1421. }
  1422. return (integer)$id;
  1423. }
  1424. /**
  1425. * Update a record in a table
  1426. *
  1427. * $dataobject is an object containing needed data
  1428. * Relies on $dataobject having a variable "id" to
  1429. * specify the record to update
  1430. *
  1431. * @uses $CFG
  1432. * @uses $db
  1433. * @param string $table The database table to be checked against.
  1434. * @param object $dataobject An object with contents equal to fieldname=>fieldvalue. Must have an entry for 'id' to map to the table specified.
  1435. * @return bool
  1436. */
  1437. function update_record($table, $dataobject) {
  1438. global $db, $CFG;
  1439. // integer value in id propery required
  1440. if (empty($dataobject->id)) {
  1441. return false;
  1442. }
  1443. $dataobject->id = (int)$dataobject->id;
  1444. /// Check we are handling a proper $dataobject
  1445. if (is_array($dataobject)) {
  1446. debugging('Warning. Wrong call to update_record(). $dataobject must be an object. array found instead', DEBUG_DEVELOPER);
  1447. $dataobject = (object)$dataobject;
  1448. } else if (is_object($dataobject)) {
  1449. // make sure there are no properties or private methods because we cast to array later,
  1450. // at the same time this undos the object references so that PHP 5 works the same as PHP 4,
  1451. // the main reason for this is BC after the dirty magic hack introduction
  1452. if ($properties = get_object_vars($dataobject)) {
  1453. $dataobject = (object)$properties;
  1454. }
  1455. unset($properties);
  1456. }
  1457. /// Extra protection against SQL injections
  1458. foreach((array)$dataobject as $k=>$v) {
  1459. $dataobject->$k = sql_magic_quotes_hack($v);
  1460. }
  1461. // Remove this record from record cache since it will change
  1462. if (!empty($CFG->rcache)) { // no === here! breaks upgrade
  1463. rcache_unset($table, $dataobject->id);
  1464. }
  1465. /// Temporary hack as part of phasing out all access to obsolete user tables XXX
  1466. if (!empty($CFG->rolesactive)) {
  1467. if (in_array($table, array('user_students', 'user_teachers', 'user_coursecreators', 'user_admins'))) {
  1468. if (debugging()) { var_dump(debug_backtrace()); }
  1469. error('This SQL relies on obsolete tables ('.$table.')! Your code must be fixed by a developer.');
  1470. }
  1471. }
  1472. /// Begin DIRTY HACK
  1473. if ($CFG->dbfamily == 'oracle') {
  1474. oracle_dirty_hack($table, $dataobject); // Convert object to the correct "empty" values for Oracle DB
  1475. }
  1476. /// End DIRTY HACK
  1477. /// Under Oracle, MSSQL and PostgreSQL we have our own update record process
  1478. /// detect all the clob/blob fields and delete them from the record being updated
  1479. /// saving them into $foundclobs and $foundblobs [$fieldname]->contents
  1480. /// They will be updated later
  1481. if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres')
  1482. && !empty($dataobject->id)) {
  1483. /// Detect lobs
  1484. $foundclobs = array();
  1485. $foundblobs = array();
  1486. db_detect_lobs($table, $dataobject, $foundclobs, $foundblobs, true);
  1487. }
  1488. // Determine all the fields in the table
  1489. if (!$columns = $db->MetaColumns($CFG->prefix . $table)) {
  1490. return false;
  1491. }
  1492. $data = (array)$dataobject;
  1493. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  1494. // Pull out data matching these fields
  1495. $update = array();
  1496. foreach ($columns as $column) {
  1497. if ($column->name == 'id') {
  1498. continue;
  1499. }
  1500. if (array_key_exists($column->name, $data)) {
  1501. $key = $column->name;
  1502. $value = $data[$key];
  1503. if (is_null($value)) {
  1504. $update[] = "$key = NULL"; // previously NULLs were not updated
  1505. } else if (is_bool($value)) {
  1506. $value = (int)$value;
  1507. $update[] = "$key = $value"; // lets keep pg happy, '' is not correct smallint MDL-13038
  1508. } else {
  1509. $update[] = "$key = '$value'"; // All incoming data is already quoted
  1510. }
  1511. }
  1512. }
  1513. /// Only if we have fields to be updated (this will prevent both wrong updates +
  1514. /// updates of only LOBs in Oracle
  1515. if ($update) {
  1516. $query = "UPDATE {$CFG->prefix}{$table} SET ".implode(',', $update)." WHERE id = {$dataobject->id}";
  1517. if (!$rs = $db->Execute($query)) {
  1518. debugging($db->ErrorMsg() .'<br /><br />'.s($query));
  1519. if (!empty($CFG->dblogerror)) {
  1520. $debug=array_shift(debug_backtrace());
  1521. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $query");
  1522. }
  1523. return false;
  1524. }
  1525. }
  1526. /// Under Oracle, MSSQL and PostgreSQL, finally, update all the Clobs and Blobs present in the record
  1527. /// if we know we have some of them in the query
  1528. if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') &&
  1529. !empty($dataobject->id) &&
  1530. (!empty($foundclobs) || !empty($foundblobs))) {
  1531. if (!db_update_lobs($table, $dataobject->id, $foundclobs, $foundblobs)) {
  1532. return false; //Some error happened while updating LOBs
  1533. }
  1534. }
  1535. return true;
  1536. }
  1537. /**
  1538. * Returns the proper SQL to do paging
  1539. *
  1540. * @uses $CFG
  1541. * @param string $page Offset page number
  1542. * @param string $recordsperpage Number of records per page
  1543. * @deprecated Moodle 1.7 use the new $limitfrom, $limitnum available in all
  1544. * the get_recordXXX() funcions.
  1545. * @return string
  1546. */
  1547. function sql_paging_limit($page, $recordsperpage) {
  1548. global $CFG;
  1549. debugging('Function sql_paging_limit() is deprecated. Replace it with the correct use of limitfrom, limitnum parameters', DEBUG_DEVELOPER);
  1550. switch ($CFG->dbfamily) {
  1551. case 'postgres':
  1552. return 'LIMIT '. $recordsperpage .' OFFSET '. $page;
  1553. default:
  1554. return 'LIMIT '. $page .','. $recordsperpage;
  1555. }
  1556. }
  1557. /**
  1558. * Returns the proper SQL to do LIKE in a case-insensitive way
  1559. *
  1560. * Note the LIKE are case sensitive for Oracle. Oracle 10g is required to use
  1561. * the caseinsensitive search using regexp_like() or NLS_COMP=LINGUISTIC :-(
  1562. * See http://docs.moodle.org/19/en/XMLDB_Problems#Case-insensitive_searches
  1563. *
  1564. * @uses $CFG
  1565. * @return string
  1566. */
  1567. function sql_ilike() {
  1568. global $CFG;
  1569. switch ($CFG->dbfamily) {
  1570. case 'postgres':
  1571. return 'ILIKE';
  1572. default:
  1573. return 'LIKE';
  1574. }
  1575. }
  1576. /**
  1577. * Returns the proper SQL to do MAX
  1578. *
  1579. * @uses $CFG
  1580. * @param string $field
  1581. * @return string
  1582. */
  1583. function sql_max($field) {
  1584. global $CFG;
  1585. switch ($CFG->dbfamily) {
  1586. default:
  1587. return "MAX($field)";
  1588. }
  1589. }
  1590. /**
  1591. * Returns the proper SQL (for the dbms in use) to concatenate $firstname and $lastname
  1592. *
  1593. * @uses $CFG
  1594. * @param string $firstname User's first name
  1595. * @param string $lastname User's last name
  1596. * @return string
  1597. */
  1598. function sql_fullname($firstname='firstname', $lastname='lastname') {
  1599. return sql_concat($firstname, "' '", $lastname);
  1600. }
  1601. /**
  1602. * Returns the proper SQL to do CONCAT between the elements passed
  1603. * Can take many parameters - just a passthrough to $db->Concat()
  1604. *
  1605. * @uses $db
  1606. * @param string $element
  1607. * @return string
  1608. */
  1609. function sql_concat() {
  1610. global $db, $CFG;
  1611. $args = func_get_args();
  1612. /// PostgreSQL requires at least one char element in the concat, let's add it
  1613. /// here (at the beginning of the array) until ADOdb fixes it
  1614. if ($CFG->dbfamily == 'postgres' && is_array($args)) {
  1615. array_unshift($args , "''");
  1616. }
  1617. return call_user_func_array(array($db, 'Concat'), $args);
  1618. }
  1619. /**
  1620. * Returns the proper SQL to do CONCAT between the elements passed
  1621. * with a given separator
  1622. *
  1623. * @uses $db
  1624. * @param string $separator
  1625. * @param array $elements
  1626. * @return string
  1627. */
  1628. function sql_concat_join($separator="' '", $elements=array()) {
  1629. global $db;
  1630. // copy to ensure pass by value
  1631. $elem = $elements;
  1632. // Intersperse $elements in the array.
  1633. // Add items to the array on the fly, walking it
  1634. // _backwards_ splicing the elements in. The loop definition
  1635. // should skip first and last positions.
  1636. for ($n=count($elem)-1; $n > 0 ; $n--) {
  1637. array_splice($elem, $n, 0, $separator);
  1638. }
  1639. return call_user_func_array(array($db, 'Concat'), $elem);
  1640. }
  1641. /**
  1642. * Returns the proper SQL to know if one field is empty.
  1643. *
  1644. * Note that the function behavior strongly relies on the
  1645. * parameters passed describing the field so, please, be accurate
  1646. * when speciffying them.
  1647. *
  1648. * Also, note that this function is not suitable to look for
  1649. * fields having NULL contents at all. It's all for empty values!
  1650. *
  1651. * This function should be applied in all the places where conditins of
  1652. * the type:
  1653. *
  1654. * ... AND fieldname = '';
  1655. *
  1656. * are being used. Final result should be:
  1657. *
  1658. * ... AND ' . sql_isempty('tablename', 'fieldname', true/false, true/false);
  1659. *
  1660. * (see parameters description below)
  1661. *
  1662. * @param string $tablename name of the table (without prefix). Not used for now but can be
  1663. * necessary in the future if we want to use some introspection using
  1664. * meta information against the DB. /// TODO ///
  1665. * @param string $fieldname name of the field we are going to check
  1666. * @param boolean $nullablefield to specify if the field us nullable (true) or no (false) in the DB
  1667. * @param boolean $textfield to specify if it is a text (also called clob) field (true) or a varchar one (false)
  1668. * @return string the sql code to be added to check for empty values
  1669. */
  1670. function sql_isempty($tablename, $fieldname, $nullablefield, $textfield) {
  1671. global $CFG;
  1672. $sql = $fieldname . " = ''";
  1673. switch ($CFG->dbfamily) {
  1674. case 'mssql':
  1675. if ($textfield) {
  1676. $sql = sql_compare_text($fieldname) . " = ''";
  1677. }
  1678. break;
  1679. case 'oracle':
  1680. if ($nullablefield) {
  1681. $sql = $fieldname . " IS NULL"; /// empties in nullable fields are stored as
  1682. } else { /// NULLs
  1683. if ($textfield) {
  1684. $sql = sql_compare_text($fieldname) . " = ' '"; /// oracle_dirty_hack inserts 1-whitespace
  1685. } else { /// in NOT NULL varchar and text columns so
  1686. $sql = $fieldname . " = ' '"; /// we need to look for that in any situation
  1687. }
  1688. }
  1689. break;
  1690. }
  1691. // Add spaces to avoid wrong SQLs due to concatenation.
  1692. // Add brackets to avoid operator precedence problems.
  1693. return ' (' . $sql . ') ';
  1694. }
  1695. /**
  1696. * Returns the proper SQL to know if one field is not empty.
  1697. *
  1698. * Note that the function behavior strongly relies on the
  1699. * parameters passed describing the field so, please, be accurate
  1700. * when speciffying them.
  1701. *
  1702. * This function should be applied in all the places where conditions of
  1703. * the type:
  1704. *
  1705. * ... AND fieldname != '';
  1706. *
  1707. * are being used. Final result should be:
  1708. *
  1709. * ... AND ' . sql_isnotempty('tablename', 'fieldname', true/false, true/false);
  1710. *
  1711. * (see parameters description below)
  1712. *
  1713. * @param string $tablename name of the table (without prefix). Not used for now but can be
  1714. * necessary in the future if we want to use some introspection using
  1715. * meta information against the DB. /// TODO ///
  1716. * @param string $fieldname name of the field we are going to check
  1717. * @param boolean $nullablefield to specify if the field us nullable (true) or no (false) in the DB
  1718. * @param boolean $textfield to specify if it is a text (also called clob) field (true) or a varchar one (false)
  1719. * @return string the sql code to be added to check for non empty values
  1720. */
  1721. function sql_isnotempty($tablename, $fieldname, $nullablefield, $textfield) {
  1722. return ' ( NOT ' . sql_isempty($tablename, $fieldname, $nullablefield, $textfield) . ') ';
  1723. }
  1724. /**
  1725. * Returns the proper AS keyword to be used to aliase columns
  1726. * SQL defines the keyword as optional and nobody but PG
  1727. * seems to require it. This function should be used inside all
  1728. * the statements using column aliases.
  1729. * Note than the use of table aliases doesn't require the
  1730. * AS keyword at all, only columns for postgres.
  1731. * @uses $CFG
  1732. * @ return string the keyword
  1733. * @deprecated Moodle 1.7 because coding guidelines now enforce to use AS in column aliases
  1734. */
  1735. function sql_as() {
  1736. global $CFG, $db;
  1737. switch ($CFG->dbfamily) {
  1738. case 'postgres':
  1739. return 'AS';
  1740. default:
  1741. return '';
  1742. }
  1743. }
  1744. /**
  1745. * Returns the empty string char used by every supported DB. To be used when
  1746. * we are searching for that values in our queries. Only Oracle uses this
  1747. * for now (will be out, once we migrate to proper NULLs if that days arrives)
  1748. */
  1749. function sql_empty() {
  1750. global $CFG;
  1751. switch ($CFG->dbfamily) {
  1752. case 'oracle':
  1753. return ' '; //Only Oracle uses 1 white-space
  1754. default:
  1755. return '';
  1756. }
  1757. }
  1758. /**
  1759. * Returns the proper substr() function for each DB
  1760. * Relies on ADOdb $db->substr property
  1761. */
  1762. function sql_substr() {
  1763. global $db;
  1764. return $db->substr;
  1765. }
  1766. /**
  1767. * Returns the SQL text to be used to compare one TEXT (clob) column with
  1768. * one varchar column, because some RDBMS doesn't support such direct
  1769. * comparisons.
  1770. * @param string fieldname the name of the TEXT field we need to order by
  1771. * @param string number of chars to use for the ordering (defaults to 32)
  1772. * @return string the piece of SQL code to be used in your statement.
  1773. */
  1774. function sql_compare_text($fieldname, $numchars=32) {
  1775. return sql_order_by_text($fieldname, $numchars);
  1776. }
  1777. /**
  1778. * Returns the SQL text to be used to order by one TEXT (clob) column, because
  1779. * some RDBMS doesn't support direct ordering of such fields.
  1780. * Note that the use or queries being ordered by TEXT columns must be minimised,
  1781. * because it's really slooooooow.
  1782. * @param string fieldname the name of the TEXT field we need to order by
  1783. * @param string number of chars to use for the ordering (defaults to 32)
  1784. * @return string the piece of SQL code to be used in your statement.
  1785. */
  1786. function sql_order_by_text($fieldname, $numchars=32) {
  1787. global $CFG;
  1788. switch ($CFG->dbfamily) {
  1789. case 'mssql':
  1790. return 'CONVERT(varchar, ' . $fieldname . ', ' . $numchars . ')';
  1791. break;
  1792. case 'oracle':
  1793. return 'dbms_lob.substr(' . $fieldname . ', ' . $numchars . ',1)';
  1794. break;
  1795. default:
  1796. return $fieldname;
  1797. }
  1798. }
  1799. /**
  1800. * Returns the SQL text to be used to calculate the length in characters of one expression.
  1801. * @param string fieldname or expression to calculate its length in characters.
  1802. * @return string the piece of SQL code to be used in the statement.
  1803. */
  1804. function sql_length($fieldname) {
  1805. global $CFG;
  1806. switch ($CFG->dbfamily) {
  1807. case 'mysql':
  1808. return 'CHAR_LENGTH(' . $fieldname . ')';
  1809. break;
  1810. case 'mssql':
  1811. return 'LEN(' . $fieldname . ')';
  1812. break;
  1813. default:
  1814. return 'LENGTH(' . $fieldname . ')';
  1815. }
  1816. }
  1817. /**
  1818. * Returns the SQL for returning searching one string for the location of another.
  1819. * @param string $needle the SQL expression that will be searched for.
  1820. * @param string $haystack the SQL expression that will be searched in.
  1821. * @return string the required SQL
  1822. */
  1823. function sql_position($needle, $haystack) {
  1824. global $CFG;
  1825. switch ($CFG->dbfamily) {
  1826. case 'mssql':
  1827. return "CHARINDEX(($needle), ($haystack))";
  1828. break;
  1829. case 'oracle':
  1830. return "INSTR(($haystack), ($needle))";
  1831. break;
  1832. default:
  1833. return "POSITION(($needle) IN ($haystack))";
  1834. }
  1835. }
  1836. /**
  1837. * Returns the SQL to be used in order to CAST one CHAR column to INTEGER.
  1838. *
  1839. * Be aware that the CHAR column you're trying to cast contains really
  1840. * int values or the RDBMS will throw an error!
  1841. *
  1842. * @param string fieldname the name of the field to be casted
  1843. * @param boolean text to specify if the original column is one TEXT (CLOB) column (true). Defaults to false.
  1844. * @return string the piece of SQL code to be used in your statement.
  1845. */
  1846. function sql_cast_char2int($fieldname, $text=false) {
  1847. global $CFG;
  1848. $sql = '';
  1849. switch ($CFG->dbfamily) {
  1850. case 'mysql':
  1851. $sql = ' CAST(' . $fieldname . ' AS SIGNED) ';
  1852. break;
  1853. case 'postgres':
  1854. $sql = ' CAST(' . $fieldname . ' AS INT) ';
  1855. break;
  1856. case 'mssql':
  1857. if (!$text) {
  1858. $sql = ' CAST(' . $fieldname . ' AS INT) ';
  1859. } else {
  1860. $sql = ' CAST(' . sql_compare_text($fieldname) . ' AS INT) ';
  1861. }
  1862. break;
  1863. case 'oracle':
  1864. if (!$text) {
  1865. $sql = ' CAST(' . $fieldname . ' AS INT) ';
  1866. } else {
  1867. $sql = ' CAST(' . sql_compare_text($fieldname) . ' AS INT) ';
  1868. }
  1869. break;
  1870. default:
  1871. $sql = ' ' . $fieldname . ' ';
  1872. }
  1873. return $sql;
  1874. }
  1875. /**
  1876. * Returns the SQL text to be used in order to perform one bitwise AND operation
  1877. * between 2 integers.
  1878. * @param integer int1 first integer in the operation
  1879. * @param integer int2 second integer in the operation
  1880. * @return string the piece of SQL code to be used in your statement.
  1881. */
  1882. function sql_bitand($int1, $int2) {
  1883. global $CFG;
  1884. switch ($CFG->dbfamily) {
  1885. case 'oracle':
  1886. return 'bitand((' . $int1 . '), (' . $int2 . '))';
  1887. break;
  1888. default:
  1889. return '((' . $int1 . ') & (' . $int2 . '))';
  1890. }
  1891. }
  1892. /**
  1893. * Returns the SQL text to be used in order to perform one bitwise OR operation
  1894. * between 2 integers.
  1895. * @param integer int1 first integer in the operation
  1896. * @param integer int2 second integer in the operation
  1897. * @return string the piece of SQL code to be used in your statement.
  1898. */
  1899. function sql_bitor($int1, $int2) {
  1900. global $CFG;
  1901. switch ($CFG->dbfamily) {
  1902. case 'oracle':
  1903. return '((' . $int1 . ') + (' . $int2 . ') - ' . sql_bitand($int1, $int2) . ')';
  1904. break;
  1905. default:
  1906. return '((' . $int1 . ') | (' . $int2 . '))';
  1907. }
  1908. }
  1909. /**
  1910. * Returns the SQL text to be used in order to perform one bitwise XOR operation
  1911. * between 2 integers.
  1912. * @param integer int1 first integer in the operation
  1913. * @param integer int2 second integer in the operation
  1914. * @return string the piece of SQL code to be used in your statement.
  1915. */
  1916. function sql_bitxor($int1, $int2) {
  1917. global $CFG;
  1918. switch ($CFG->dbfamily) {
  1919. case 'oracle':
  1920. return '(' . sql_bitor($int1, $int2) . ' - ' . sql_bitand($int1, $int2) . ')';
  1921. break;
  1922. case 'postgres':
  1923. return '((' . $int1 . ') # (' . $int2 . '))';
  1924. break;
  1925. default:
  1926. return '((' . $int1 . ') ^ (' . $int2 . '))';
  1927. }
  1928. }
  1929. /**
  1930. * Returns the SQL text to be used in order to perform one bitwise NOT operation
  1931. * with 1 integer.
  1932. * @param integer int1 integer in the operation
  1933. * @return string the piece of SQL code to be used in your statement.
  1934. */
  1935. function sql_bitnot($int1) {
  1936. global $CFG;
  1937. switch ($CFG->dbfamily) {
  1938. case 'oracle':
  1939. return '((0 - (' . $int1 . ')) - 1)';
  1940. break;
  1941. default:
  1942. return '(~(' . $int1 . '))';
  1943. }
  1944. }
  1945. /**
  1946. * Returns the FROM clause required by some DBs in all SELECT statements
  1947. * To be used in queries not having FROM clause to provide cross_db
  1948. */
  1949. function sql_null_from_clause() {
  1950. global $CFG;
  1951. switch ($CFG->dbfamily) {
  1952. case 'oracle':
  1953. return ' FROM dual';
  1954. break;
  1955. default:
  1956. return '';
  1957. }
  1958. }
  1959. /**
  1960. * Returns the correct CEIL expression applied to fieldname
  1961. * @param string fieldname the field (or expression) we are going to ceil
  1962. * @return string the piece of SQL code to be used in your ceiling statement
  1963. */
  1964. function sql_ceil($fieldname) {
  1965. global $CFG;
  1966. switch ($CFG->dbfamily) {
  1967. case 'mssql':
  1968. return ' CEILING(' . $fieldname . ')';
  1969. break;
  1970. default:
  1971. return ' CEIL(' . $fieldname . ')';
  1972. }
  1973. }
  1974. /**
  1975. * This hack prevents some types of SQL injections, no code should rely on this,
  1976. * do not forget to use addslashes() and addslashes_recursive() properly!
  1977. *
  1978. * The performance cost is negligible considering the security benefits and DB requests cost.
  1979. *
  1980. * @param mixed $value sql parameter value (hopefully with magic quotes)
  1981. * @return mixed sanitised value - added magic quotes if accidentally missing
  1982. */
  1983. function sql_magic_quotes_hack($value) {
  1984. if ($value === null or $value === '') {
  1985. // performance shortcut
  1986. return $value;
  1987. }
  1988. // ignore stuff that can not be converted to string, catchable fatal error will be displayed elsewhere,
  1989. // this is intentional because we want to get the same errors as before this magic hack
  1990. if (is_object($value)) {
  1991. if (!method_exists($value, '__toString')) {
  1992. // ignore - we can not cast object to string, error will be triggered elsewhere
  1993. return $value;
  1994. }
  1995. } else if (!is_string($value)) {
  1996. // no sql injection possible in other non-string values
  1997. return $value;
  1998. }
  1999. // note: this does not change content if the content is properly escaped,
  2000. // the result is different only for strings with missing magic quotes!
  2001. return addslashes(stripslashes($value));
  2002. }
  2003. /**
  2004. * Prepare a SQL WHERE clause to select records where the given fields match the given values.
  2005. *
  2006. * Prepares a where clause of the form
  2007. * WHERE field1 = value1 AND field2 = value2 AND field3 = value3
  2008. * except that you need only specify as many arguments (zero to three) as you need.
  2009. *
  2010. * @param string $field1 the first field to check (optional).
  2011. * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
  2012. * @param string $field2 the second field to check (optional).
  2013. * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
  2014. * @param string $field3 the third field to check (optional).
  2015. * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
  2016. */
  2017. function where_clause($field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
  2018. $value1 = sql_magic_quotes_hack($value1);
  2019. $value2 = sql_magic_quotes_hack($value2);
  2020. $value3 = sql_magic_quotes_hack($value3);
  2021. if ($field1) {
  2022. $select = is_null($value1) ? "WHERE $field1 IS NULL" : "WHERE $field1 = '$value1'";
  2023. if ($field2) {
  2024. $select .= is_null($value2) ? " AND $field2 IS NULL" : " AND $field2 = '$value2'";
  2025. if ($field3) {
  2026. $select .= is_null($value3) ? " AND $field3 IS NULL" : " AND $field3 = '$value3'";
  2027. }
  2028. }
  2029. } else {
  2030. $select = '';
  2031. }
  2032. return $select;
  2033. }
  2034. /**
  2035. * Get the data type of a table column, using an ADOdb MetaType() call.
  2036. *
  2037. * @uses $CFG
  2038. * @uses $db
  2039. * @param string $table The name of the database table
  2040. * @param string $column The name of the field in the table
  2041. * @return string Field type or false if error
  2042. */
  2043. function column_type($table, $column) {
  2044. global $CFG, $db;
  2045. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  2046. $sql = 'SELECT '.$column.' FROM '.$CFG->prefix.$table.' WHERE 1=2';
  2047. if(!$rs = $db->Execute($sql)) {
  2048. debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
  2049. if (!empty($CFG->dblogerror)) {
  2050. $debug=array_shift(debug_backtrace());
  2051. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
  2052. }
  2053. return false;
  2054. }
  2055. $field = $rs->FetchField(0);
  2056. return $rs->MetaType($field->type);
  2057. }
  2058. /**
  2059. * This function will execute an array of SQL commands, returning
  2060. * true/false if any error is found and stopping/continue as desired.
  2061. * It's widely used by all the ddllib.php functions
  2062. *
  2063. * @param array sqlarr array of sql statements to execute
  2064. * @param boolean continue to specify if must continue on error (true) or stop (false)
  2065. * @param boolean feedback to specify to show status info (true) or not (false)
  2066. * @param boolean true if everything was ok, false if some error was found
  2067. */
  2068. function execute_sql_arr($sqlarr, $continue=true, $feedback=true) {
  2069. if (!is_array($sqlarr)) {
  2070. return false;
  2071. }
  2072. $status = true;
  2073. foreach($sqlarr as $sql) {
  2074. if (!execute_sql($sql, $feedback)) {
  2075. $status = false;
  2076. if (!$continue) {
  2077. break;
  2078. }
  2079. }
  2080. }
  2081. return $status;
  2082. }
  2083. /**
  2084. * This internal function, called from setup.php, sets all the configuration
  2085. * needed to work properly against any DB. It setups connection encoding
  2086. * and some other variables.
  2087. *
  2088. * This function must contain the init code needed for each dbtype supported.
  2089. */
  2090. function configure_dbconnection() {
  2091. global $CFG, $db;
  2092. switch ($CFG->dbtype) {
  2093. case 'mysql':
  2094. case 'mysqli':
  2095. $db->Execute("SET NAMES 'utf8'");
  2096. break;
  2097. case 'postgres7':
  2098. $db->Execute("SET NAMES 'utf8'");
  2099. break;
  2100. case 'mssql':
  2101. case 'mssql_n':
  2102. case 'odbc_mssql':
  2103. /// No need to set charset. It must be specified in the driver conf
  2104. /// Allow quoted identifiers
  2105. $db->Execute('SET QUOTED_IDENTIFIER ON');
  2106. /// Force ANSI nulls so the NULL check was done by IS NULL and NOT IS NULL
  2107. /// instead of equal(=) and distinct(<>) simbols
  2108. $db->Execute('SET ANSI_NULLS ON');
  2109. /// Enable sybase quotes, so addslashes and stripslashes will use "'"
  2110. ini_set('magic_quotes_sybase', '1');
  2111. /// NOTE: Not 100% useful because GPC has been addslashed with the setting off
  2112. /// so IT'S MANDATORY TO CHANGE THIS UNDER php.ini or .htaccess for this DB
  2113. /// or to turn off magic_quotes to allow Moodle to do it properly
  2114. break;
  2115. case 'oci8po':
  2116. /// No need to set charset. It must be specified by the NLS_LANG env. variable
  2117. /// Enable sybase quotes, so addslashes and stripslashes will use "'"
  2118. ini_set('magic_quotes_sybase', '1');
  2119. /// NOTE: Not 100% useful because GPC has been addslashed with the setting off
  2120. /// so IT'S MANDATORY TO ENABLE THIS UNDER php.ini or .htaccess for this DB
  2121. /// or to turn off magic_quotes to allow Moodle to do it properly
  2122. /// Now set the decimal separator to DOT, Moodle & PHP will always send floats to
  2123. /// DB using DOTS. Manually introduced floats (if using other characters) must be
  2124. /// converted back to DOTs (like gradebook does)
  2125. $db->Execute("ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,'");
  2126. break;
  2127. }
  2128. }
  2129. /**
  2130. * This function will handle all the records before being inserted/updated to DB for Oracle
  2131. * installations. This is because the "special feature" of Oracle where the empty string is
  2132. * equal to NULL and this presents a problem with all our currently NOT NULL default '' fields.
  2133. *
  2134. * Once Moodle DB will be free of this sort of false NOT NULLS, this hack could be removed safely
  2135. *
  2136. * Note that this function is 100% private and should be used, exclusively by DML functions
  2137. * in this file. Also, this is considered a DIRTY HACK to be removed when possible. (stronk7)
  2138. *
  2139. * This function is private and must not be used outside dmllib at all
  2140. *
  2141. * @param $table string the table where the record is going to be inserted/updated (without prefix)
  2142. * @param $dataobject object the object to be inserted/updated
  2143. * @param $usecache boolean flag to determinate if we must use the per request cache of metadata
  2144. * true to use it, false to ignore and delete it
  2145. */
  2146. function oracle_dirty_hack ($table, &$dataobject, $usecache = true) {
  2147. global $CFG, $db, $metadata_cache;
  2148. /// Init and delete metadata cache
  2149. if (!isset($metadata_cache) || !$usecache) {
  2150. $metadata_cache = array();
  2151. }
  2152. /// For Oracle DB, empty strings are converted to NULLs in DB
  2153. /// and this breaks a lot of NOT NULL columns currenty Moodle. In the future it's
  2154. /// planned to move some of them to NULL, if they must accept empty values and this
  2155. /// piece of code will become less and less used. But, for now, we need it.
  2156. /// What we are going to do is to examine all the data being inserted and if it's
  2157. /// an empty string (NULL for Oracle) and the field is defined as NOT NULL, we'll modify
  2158. /// such data in the best form possible ("0" for booleans and numbers and " " for the
  2159. /// rest of strings. It isn't optimal, but the only way to do so.
  2160. /// In the oppsite, when retrieving records from Oracle, we'll decode " " back to
  2161. /// empty strings to allow everything to work properly. DIRTY HACK.
  2162. /// If the db isn't Oracle, return without modif
  2163. if ( $CFG->dbfamily != 'oracle') {
  2164. return;
  2165. }
  2166. /// Get Meta info to know what to change, using the cached meta if exists
  2167. if (!isset($metadata_cache[$table])) {
  2168. $metadata_cache[$table] = array_change_key_case($db->MetaColumns($CFG->prefix . $table), CASE_LOWER);
  2169. }
  2170. $columns = $metadata_cache[$table];
  2171. /// Iterate over all the fields in the insert, transforming values
  2172. /// in the best possible form
  2173. foreach ($dataobject as $fieldname => $fieldvalue) {
  2174. /// If the field doesn't exist in metadata, skip
  2175. if (!isset($columns[strtolower($fieldname)])) {
  2176. continue;
  2177. }
  2178. /// If the field ins't VARCHAR or CLOB, skip
  2179. if ($columns[strtolower($fieldname)]->type != 'VARCHAR2' && $columns[strtolower($fieldname)]->type != 'CLOB') {
  2180. continue;
  2181. }
  2182. /// If the field isn't NOT NULL, skip (it's nullable, so accept empty values)
  2183. if (!$columns[strtolower($fieldname)]->not_null) {
  2184. continue;
  2185. }
  2186. /// If the value isn't empty, skip
  2187. if (!empty($fieldvalue)) {
  2188. continue;
  2189. }
  2190. /// Now, we have one empty value, going to be inserted to one NOT NULL, VARCHAR2 or CLOB field
  2191. /// Try to get the best value to be inserted
  2192. /// The '0' string doesn't need any transformation, skip
  2193. if ($fieldvalue === '0') {
  2194. continue;
  2195. }
  2196. /// Transformations start
  2197. if (gettype($fieldvalue) == 'boolean') {
  2198. $dataobject->$fieldname = '0'; /// Transform false to '0' that evaluates the same for PHP
  2199. } else if (gettype($fieldvalue) == 'integer') {
  2200. $dataobject->$fieldname = '0'; /// Transform 0 to '0' that evaluates the same for PHP
  2201. } else if (gettype($fieldvalue) == 'NULL') {
  2202. $dataobject->$fieldname = '0'; /// Transform NULL to '0' that evaluates the same for PHP
  2203. } else if ($fieldvalue === '') {
  2204. $dataobject->$fieldname = ' '; /// Transform '' to ' ' that DONT'T EVALUATE THE SAME
  2205. /// (we'll transform back again on get_records_XXX functions and others)!!
  2206. }
  2207. }
  2208. }
  2209. /// End of DIRTY HACK
  2210. /**
  2211. * This function will search for all the CLOBs and BLOBs fields passed in the dataobject, replacing
  2212. * their contents by the fixed strings '@#CLOB#@' and '@#BLOB#@' and returning one array for all the
  2213. * found CLOBS and another for all the found BLOBS
  2214. * Used by Oracle drivers to perform the two-step insertion/update of LOBs and
  2215. * by MSSQL to perform the same exclusively for BLOBs (IMAGE fields)
  2216. *
  2217. * This function is private and must not be used outside dmllib at all
  2218. *
  2219. * @param $table string the table where the record is going to be inserted/updated (without prefix)
  2220. * @param $dataobject object the object to be inserted/updated
  2221. * @param $clobs array of clobs detected
  2222. * @param $dataobject array of blobs detected
  2223. * @param $unset boolean to specify if we must unset found LOBs from the original object (true) or
  2224. * just return them modified to @#CLOB#@ and @#BLOB#@ (false)
  2225. * @param $usecache boolean flag to determinate if we must use the per request cache of metadata
  2226. * true to use it, false to ignore and delete it
  2227. */
  2228. function db_detect_lobs ($table, &$dataobject, &$clobs, &$blobs, $unset = false, $usecache = true) {
  2229. global $CFG, $db, $metadata_cache;
  2230. $dataarray = (array)$dataobject; //Convert to array. It's supposed that PHP 4.3 doesn't iterate over objects
  2231. /// Initial configuration, based on DB
  2232. switch ($CFG->dbfamily) {
  2233. case 'oracle':
  2234. $clobdbtype = 'CLOB'; //Name of clobs for this DB
  2235. $blobdbtype = 'BLOB'; //Name of blobs for this DB
  2236. break;
  2237. case 'mssql':
  2238. $clobdbtype = 'NOTPROCESSES'; //Name of clobs for this DB (under mssql flavours we don't process CLOBS)
  2239. $blobdbtype = 'IMAGE'; //Name of blobs for this DB
  2240. break;
  2241. case 'postgres':
  2242. $clobdbtype = 'NOTPROCESSES'; //Name of clobs for this DB (under postgres flavours we don't process CLOBS)
  2243. $blobdbtype = 'BYTEA'; //Name of blobs for this DB
  2244. break;
  2245. default:
  2246. return; //Other DB doesn't need this two step to happen, prevent continue
  2247. }
  2248. /// Init and delete metadata cache
  2249. if (!isset($metadata_cache) || !$usecache) {
  2250. $metadata_cache = array();
  2251. }
  2252. /// Get Meta info to know what to change, using the cached meta if exists
  2253. if (!isset($metadata_cache[$table])) {
  2254. $metadata_cache[$table] = array_change_key_case($db->MetaColumns($CFG->prefix . $table), CASE_LOWER);
  2255. }
  2256. $columns = $metadata_cache[$table];
  2257. foreach ($dataarray as $fieldname => $fieldvalue) {
  2258. /// If the field doesn't exist in metadata, skip
  2259. if (!isset($columns[strtolower($fieldname)])) {
  2260. continue;
  2261. }
  2262. /// If the field is CLOB, update its value to '@#CLOB#@' and store it in the $clobs array
  2263. if (strtoupper($columns[strtolower($fieldname)]->type) == $clobdbtype) {
  2264. /// Oracle optimization. CLOBs under 4000cc can be directly inserted (no need to apply 2-phases to them)
  2265. if ($CFG->dbfamily == 'oracle' && strlen($dataobject->$fieldname) < 4000) {
  2266. continue;
  2267. }
  2268. $clobs[$fieldname] = $dataobject->$fieldname;
  2269. if ($unset) {
  2270. unset($dataobject->$fieldname);
  2271. } else {
  2272. $dataobject->$fieldname = '@#CLOB#@';
  2273. }
  2274. continue;
  2275. }
  2276. /// If the field is BLOB OR IMAGE OR BYTEA, update its value to '@#BLOB#@' and store it in the $blobs array
  2277. if (strtoupper($columns[strtolower($fieldname)]->type) == $blobdbtype) {
  2278. $blobs[$fieldname] = $dataobject->$fieldname;
  2279. if ($unset) {
  2280. unset($dataobject->$fieldname);
  2281. } else {
  2282. $dataobject->$fieldname = '@#BLOB#@';
  2283. }
  2284. continue;
  2285. }
  2286. }
  2287. }
  2288. /**
  2289. * This function will iterate over $clobs and $blobs array, executing the needed
  2290. * UpdateClob() and UpdateBlob() ADOdb function calls to store LOBs contents properly
  2291. * Records to be updated are always searched by PK (id always!)
  2292. *
  2293. * Used by Orace CLOBS and BLOBS and MSSQL IMAGES
  2294. *
  2295. * This function is private and must not be used outside dmllib at all
  2296. *
  2297. * @param $table string the table where the record is going to be inserted/updated (without prefix)
  2298. * @param $sqlcondition mixed value defining the records to be LOB-updated. It it's a number, must point
  2299. * to the PK og the table (id field), else it's processed as one harcoded SQL condition (WHERE clause)
  2300. * @param $clobs array of clobs to be updated
  2301. * @param $blobs array of blobs to be updated
  2302. */
  2303. function db_update_lobs ($table, $sqlcondition, &$clobs, &$blobs) {
  2304. global $CFG, $db;
  2305. $status = true;
  2306. /// Initial configuration, based on DB
  2307. switch ($CFG->dbfamily) {
  2308. case 'oracle':
  2309. $clobdbtype = 'CLOB'; //Name of clobs for this DB
  2310. $blobdbtype = 'BLOB'; //Name of blobs for this DB
  2311. break;
  2312. case 'mssql':
  2313. $clobdbtype = 'NOTPROCESSES'; //Name of clobs for this DB (under mssql flavours we don't process CLOBS)
  2314. $blobdbtype = 'IMAGE'; //Name of blobs for this DB
  2315. break;
  2316. case 'postgres':
  2317. $clobdbtype = 'NOTPROCESSES'; //Name of clobs for this DB (under postgres flavours we don't process CLOBS)
  2318. $blobdbtype = 'BYTEA'; //Name of blobs for this DB
  2319. break;
  2320. default:
  2321. return; //Other DB doesn't need this two step to happen, prevent continue
  2322. }
  2323. /// Calculate the update sql condition
  2324. if (is_numeric($sqlcondition)) { /// If passing a number, it's the PK of the table (id)
  2325. $sqlcondition = 'id=' . $sqlcondition;
  2326. } else { /// Else, it's a formal standard SQL condition, we try to delete the WHERE in case it exists
  2327. $sqlcondition = trim(preg_replace('/^WHERE/is', '', trim($sqlcondition)));
  2328. }
  2329. /// Update all the clobs
  2330. if ($clobs) {
  2331. foreach ($clobs as $key => $value) {
  2332. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; }; /// Count the extra updates in PERF
  2333. /// Oracle CLOBs doesn't like quoted strings (are inserted via prepared statemets)
  2334. if ($CFG->dbfamily == 'oracle') {
  2335. $value = stripslashes_safe($value);
  2336. }
  2337. if (!$db->UpdateClob($CFG->prefix.$table, $key, $value, $sqlcondition)) {
  2338. $status = false;
  2339. $statement = "UpdateClob('$CFG->prefix$table', '$key', '" . substr($value, 0, 100) . "...', '$sqlcondition')";
  2340. debugging($db->ErrorMsg() ."<br /><br />".s($statement));
  2341. if (!empty($CFG->dblogerror)) {
  2342. $debug=array_shift(debug_backtrace());
  2343. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $statement");
  2344. }
  2345. }
  2346. }
  2347. }
  2348. /// Update all the blobs
  2349. if ($blobs) {
  2350. foreach ($blobs as $key => $value) {
  2351. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; }; /// Count the extra updates in PERF
  2352. /// Oracle, MSSQL and PostgreSQL BLOBs doesn't like quoted strings (are inserted via prepared statemets)
  2353. if ($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') {
  2354. $value = stripslashes_safe($value);
  2355. }
  2356. if(!$db->UpdateBlob($CFG->prefix.$table, $key, $value, $sqlcondition)) {
  2357. $status = false;
  2358. $statement = "UpdateBlob('$CFG->prefix$table', '$key', '" . substr($value, 0, 100) . "...', '$sqlcondition')";
  2359. debugging($db->ErrorMsg() ."<br /><br />".s($statement));
  2360. if (!empty($CFG->dblogerror)) {
  2361. $debug=array_shift(debug_backtrace());
  2362. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $statement");
  2363. }
  2364. }
  2365. }
  2366. }
  2367. return $status;
  2368. }
  2369. /**
  2370. * Set cached record.
  2371. *
  2372. * If you have called rcache_getforfill() before, it will also
  2373. * release the lock.
  2374. *
  2375. * This function is private and must not be used outside dmllib at all
  2376. *
  2377. * @param $table string
  2378. * @param $id integer
  2379. * @param $rec obj
  2380. * @return bool
  2381. */
  2382. function rcache_set($table, $id, $rec) {
  2383. global $CFG, $MCACHE, $rcache;
  2384. if ($CFG->cachetype === 'internal') {
  2385. if (!isset($rcache->data[$table])) {
  2386. $rcache->data[$table] = array();
  2387. }
  2388. if (!isset($rcache->data[$table][$id]) and count($rcache->data[$table]) > $CFG->intcachemax) {
  2389. // release oldes record
  2390. reset($rcache->data[$table]);
  2391. $key = key($rcache->data[$table]);
  2392. unset($rcache->data[$table][$key]);
  2393. }
  2394. $rcache->data[$table][$id] = clone($rec);
  2395. } else {
  2396. $key = $table . '|' . $id;
  2397. if (isset($MCACHE)) {
  2398. // $table is a flag used to mark
  2399. // a table as dirty & uncacheable
  2400. // when an UPDATE or DELETE not bound by ID
  2401. // is taking place
  2402. if (!$MCACHE->get($table)) {
  2403. // this will also release the _forfill lock
  2404. $MCACHE->set($key, $rec, $CFG->rcachettl);
  2405. }
  2406. }
  2407. }
  2408. return true;
  2409. }
  2410. /**
  2411. * Unset cached record if it exists.
  2412. *
  2413. * This function is private and must not be used outside dmllib at all
  2414. *
  2415. * @param $table string
  2416. * @param $id integer
  2417. * @return bool
  2418. */
  2419. function rcache_unset($table, $id) {
  2420. global $CFG, $MCACHE, $rcache;
  2421. if ($CFG->cachetype === 'internal') {
  2422. if (isset($rcache->data[$table][$id])) {
  2423. unset($rcache->data[$table][$id]);
  2424. }
  2425. } else {
  2426. $key = $table . '|' . $id;
  2427. if (isset($MCACHE)) {
  2428. $MCACHE->delete($key);
  2429. }
  2430. }
  2431. return true;
  2432. }
  2433. /**
  2434. * Get cached record if available. ONLY use if you
  2435. * are trying to get the cached record and will NOT
  2436. * fetch it yourself if not cached.
  2437. *
  2438. * Use rcache_getforfill() if you are going to fetch
  2439. * the record if not cached...
  2440. *
  2441. * This function is private and must not be used outside dmllib at all
  2442. *
  2443. * @param $table string
  2444. * @param $id integer
  2445. * @return mixed object-like record on cache hit, false otherwise
  2446. */
  2447. function rcache_get($table, $id) {
  2448. global $CFG, $MCACHE, $rcache;
  2449. if ($CFG->cachetype === 'internal') {
  2450. if (isset($rcache->data[$table][$id])) {
  2451. $rcache->hits++;
  2452. return clone($rcache->data[$table][$id]);
  2453. } else {
  2454. $rcache->misses++;
  2455. return false;
  2456. }
  2457. }
  2458. if (isset($MCACHE)) {
  2459. $key = $table . '|' . $id;
  2460. // we set $table as a flag used to mark
  2461. // a table as dirty & uncacheable
  2462. // when an UPDATE or DELETE not bound by ID
  2463. // is taking place
  2464. if ($MCACHE->get($table)) {
  2465. $rcache->misses++;
  2466. return false;
  2467. } else {
  2468. $rec = $MCACHE->get($key);
  2469. if (!empty($rec)) {
  2470. $rcache->hits++;
  2471. return $rec;
  2472. } else {
  2473. $rcache->misses++;
  2474. return false;
  2475. }
  2476. }
  2477. }
  2478. return false;
  2479. }
  2480. /**
  2481. * Get cached record if available. In most cases you want
  2482. * to use this function -- namely if you are trying to get
  2483. * the cached record and will fetch it yourself if not cached.
  2484. * (and set the cache ;-)
  2485. *
  2486. * Uses the getforfill caching mechanism. See lib/eaccelerator.class.php
  2487. * for a detailed description of the technique.
  2488. *
  2489. * Note: if you call rcache_getforfill() you are making an implicit promise
  2490. * that if the cache is empty, you will later populate it, or cancel the promise
  2491. * calling rcache_releaseforfill();
  2492. *
  2493. * This function is private and must not be used outside dmllib at all
  2494. *
  2495. * @param $table string
  2496. * @param $id integer
  2497. * @return mixed object-like record on cache hit, false otherwise
  2498. */
  2499. function rcache_getforfill($table, $id) {
  2500. global $CFG, $MCACHE, $rcache;
  2501. if ($CFG->cachetype === 'internal') {
  2502. return rcache_get($table, $id);
  2503. }
  2504. if (isset($MCACHE)) {
  2505. $key = $table . '|' . $id;
  2506. // if $table is set - we won't take the
  2507. // lock either
  2508. if ($MCACHE->get($table)) {
  2509. $rcache->misses++;
  2510. return false;
  2511. }
  2512. $rec = $MCACHE->getforfill($key);
  2513. if (!empty($rec)) {
  2514. $rcache->hits++;
  2515. return $rec;
  2516. }
  2517. $rcache->misses++;
  2518. return false;
  2519. }
  2520. return false;
  2521. }
  2522. /**
  2523. * Release the exclusive lock obtained by
  2524. * rcache_getforfill(). See rcache_getforfill()
  2525. * for more details.
  2526. *
  2527. * This function is private and must not be used outside dmllib at all
  2528. *
  2529. * @param $table string
  2530. * @param $id integer
  2531. * @return bool
  2532. */
  2533. function rcache_releaseforfill($table, $id) {
  2534. global $CFG, $MCACHE;
  2535. if (isset($MCACHE)) {
  2536. $key = $table . '|' . $id;
  2537. return $MCACHE->releaseforfill($key);
  2538. }
  2539. return true;
  2540. }
  2541. /**
  2542. * Remove or invalidate all rcache entries related to
  2543. * a table. Not all caching mechanisms cluster entries
  2544. * by table so in those cases we use alternative strategies.
  2545. *
  2546. * This function is private and must not be used outside dmllib at all
  2547. *
  2548. * @param $table string the table to invalidate records for
  2549. * @return bool
  2550. */
  2551. function rcache_unset_table ($table) {
  2552. global $CFG, $MCACHE, $rcache;
  2553. if ($CFG->cachetype === 'internal') {
  2554. if (isset($rcache->data[$table])) {
  2555. unset($rcache->data[$table]);
  2556. }
  2557. return true;
  2558. }
  2559. if (isset($MCACHE)) {
  2560. // at least as long as content keys to ensure they expire
  2561. // before the dirty flag
  2562. $MCACHE->set($table, true, $CFG->rcachettl);
  2563. }
  2564. return true;
  2565. }
  2566. ?>