PageRenderTime 41ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/moodle/lib/dmllib.php

https://bitbucket.org/geek745/moodle-db2
PHP | 2868 lines | 1368 code | 338 blank | 1162 comment | 418 complexity | 4ddce1df93c3545681376329f37cbbf4 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause, LGPL-2.0
  1. <?php // $Id: dmllib.php,v 1.116.2.33 2009/09/26 11:55:57 skodak 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/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. $select = "$field = '$value'";
  482. } else {
  483. $select = '';
  484. }
  485. return get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
  486. }
  487. /**
  488. * Get a number of records as an ADODB RecordSet.
  489. *
  490. * If given, $select is used as the SELECT parameter in the SQL query,
  491. * otherwise all records from the table are returned.
  492. *
  493. * Other arguments and the return type as for @see function get_recordset.
  494. *
  495. * @uses $CFG
  496. * @param string $table the table to query.
  497. * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
  498. * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
  499. * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
  500. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  501. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  502. * @return mixed an ADODB RecordSet object, or false if an error occured.
  503. */
  504. function get_recordset_select($table, $select='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  505. global $CFG;
  506. if ($select) {
  507. $select = ' WHERE '. $select;
  508. }
  509. if ($sort) {
  510. $sort = ' ORDER BY '. $sort;
  511. }
  512. return get_recordset_sql('SELECT '. $fields .' FROM '. $CFG->prefix . $table . $select . $sort, $limitfrom, $limitnum);
  513. }
  514. /**
  515. * Get a number of records as an ADODB RecordSet.
  516. *
  517. * Only records where $field takes one of the values $values are returned.
  518. * $values should be a comma-separated list of values, for example "4,5,6,10"
  519. * or "'foo','bar','baz'".
  520. *
  521. * Other arguments and the return type as for @see function get_recordset.
  522. *
  523. * @param string $table the table to query.
  524. * @param string $field a field to check (optional).
  525. * @param string $values comma separated list of values the field must have (requred if field is given, else optional).
  526. * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
  527. * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
  528. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  529. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  530. * @return mixed an ADODB RecordSet object, or false if an error occured.
  531. */
  532. function get_recordset_list($table, $field='', $values='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  533. if ($field) {
  534. $select = "$field IN ($values)";
  535. } else {
  536. $select = '';
  537. }
  538. return get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
  539. }
  540. /**
  541. * Get a number of records as an ADODB RecordSet. $sql must be a complete SQL query.
  542. * Since this method is a little less readable, use of it should be restricted to
  543. * code where it's possible there might be large datasets being returned. For known
  544. * small datasets use get_records_sql - it leads to simpler code.
  545. *
  546. * The return type is as for @see function get_recordset.
  547. *
  548. * @uses $CFG
  549. * @uses $db
  550. * @param string $sql the SQL select query to execute.
  551. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  552. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  553. * @return mixed an ADODB RecordSet object, or false if an error occured.
  554. */
  555. function get_recordset_sql($sql, $limitfrom=null, $limitnum=null) {
  556. global $CFG, $db;
  557. if (empty($db)) {
  558. return false;
  559. }
  560. /// Temporary hack as part of phasing out all access to obsolete user tables XXX
  561. if (!empty($CFG->rolesactive)) {
  562. if (strpos($sql, ' '.$CFG->prefix.'user_students ') ||
  563. strpos($sql, ' '.$CFG->prefix.'user_teachers ') ||
  564. strpos($sql, ' '.$CFG->prefix.'user_coursecreators ') ||
  565. strpos($sql, ' '.$CFG->prefix.'user_admins ')) {
  566. if (debugging()) { var_dump(debug_backtrace()); }
  567. error('This SQL relies on obsolete tables! Your code must be fixed by a developer.');
  568. }
  569. }
  570. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  571. if ($limitfrom || $limitnum) {
  572. ///Special case, 0 must be -1 for ADOdb
  573. $limitfrom = empty($limitfrom) ? -1 : $limitfrom;
  574. $limitnum = empty($limitnum) ? -1 : $limitnum;
  575. $rs = $db->SelectLimit($sql, $limitnum, $limitfrom);
  576. } else {
  577. $rs = $db->Execute($sql);
  578. }
  579. if (!$rs) {
  580. debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
  581. if (!empty($CFG->dblogerror)) {
  582. $debug=array_shift(debug_backtrace());
  583. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql with limits ($limitfrom, $limitnum)");
  584. }
  585. return false;
  586. }
  587. return $rs;
  588. }
  589. /**
  590. * Utility function used by the following 4 methods. Note that for this to work, the first column
  591. * in the recordset must contain unique values, as it is used as the key to the associative array.
  592. *
  593. * @param object an ADODB RecordSet object.
  594. * @return mixed mixed an array of objects, or false if an error occured or the RecordSet was empty.
  595. */
  596. function recordset_to_array($rs) {
  597. global $CFG;
  598. $debugging = debugging('', DEBUG_DEVELOPER);
  599. if ($rs && !rs_EOF($rs)) {
  600. $objects = array();
  601. /// First of all, we are going to get the name of the first column
  602. /// to introduce it back after transforming the recordset to assoc array
  603. /// See http://docs.moodle.org/en/XMLDB_Problems, fetch mode problem.
  604. $firstcolumn = $rs->FetchField(0);
  605. /// Get the whole associative array
  606. if ($records = $rs->GetAssoc(true)) {
  607. foreach ($records as $key => $record) {
  608. /// Really DIRTY HACK for Oracle, but it's the only way to make it work
  609. /// until we got all those NOT NULL DEFAULT '' out from Moodle
  610. if ($CFG->dbfamily == 'oracle') {
  611. array_walk($record, 'onespace2empty');
  612. }
  613. /// End of DIRTY HACK
  614. $record[$firstcolumn->name] = $key;/// Re-add the assoc field
  615. if ($debugging && array_key_exists($key, $objects)) {
  616. 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);
  617. }
  618. $objects[$key] = (object) $record; /// To object
  619. }
  620. return $objects;
  621. /// Fallback in case we only have 1 field in the recordset. MDL-5877
  622. } else if ($rs->_numOfFields == 1 && $records = $rs->GetRows()) {
  623. foreach ($records as $key => $record) {
  624. /// Really DIRTY HACK for Oracle, but it's the only way to make it work
  625. /// until we got all those NOT NULL DEFAULT '' out from Moodle
  626. if ($CFG->dbfamily == 'oracle') {
  627. array_walk($record, 'onespace2empty');
  628. }
  629. /// End of DIRTY HACK
  630. if ($debugging && array_key_exists($record[$firstcolumn->name], $objects)) {
  631. 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);
  632. }
  633. $objects[$record[$firstcolumn->name]] = (object) $record; /// The key is the first column value (like Assoc)
  634. }
  635. return $objects;
  636. } else {
  637. return false;
  638. }
  639. } else {
  640. return false;
  641. }
  642. }
  643. /**
  644. * This function is used to get the current record from the recordset. It
  645. * doesn't advance the recordset position. You'll need to do that by
  646. * using the rs_next_record($recordset) function.
  647. * @param ADORecordSet the recordset to fetch current record from
  648. * @return ADOFetchObj the object containing the fetched information
  649. */
  650. function rs_fetch_record(&$rs) {
  651. global $CFG;
  652. if (!$rs) {
  653. debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
  654. return false;
  655. }
  656. $rec = $rs->FetchObj(); //Retrieve record as object without advance the pointer
  657. if ($rs->EOF) { //FetchObj requires manual checking of EOF to detect if it's the last record
  658. $rec = false;
  659. } else {
  660. /// DIRTY HACK to retrieve all the ' ' (1 space) fields converted back
  661. /// to '' (empty string) for Oracle. It's the only way to work with
  662. /// all those NOT NULL DEFAULT '' fields until we definetively delete them
  663. if ($CFG->dbfamily == 'oracle') {
  664. $recarr = (array)$rec; /// Cast to array
  665. array_walk($recarr, 'onespace2empty');
  666. $rec = (object)$recarr;/// Cast back to object
  667. }
  668. /// End DIRTY HACK
  669. }
  670. return $rec;
  671. }
  672. /**
  673. * This function is used to advance the pointer of the recordset
  674. * to its next position/record.
  675. * @param ADORecordSet the recordset to be moved to the next record
  676. * @return boolean true if the movement was successful and false if not (end of recordset)
  677. */
  678. function rs_next_record(&$rs) {
  679. if (!$rs) {
  680. debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
  681. return false;
  682. }
  683. return $rs->MoveNext(); //Move the pointer to the next record
  684. }
  685. /**
  686. * This function is used to get the current record from the recordset. It
  687. * does advance the recordset position.
  688. * This is the prefered way to iterate over recordsets with code blocks like this:
  689. *
  690. * $rs = get_recordset('SELECT .....');
  691. * while ($rec = rs_fetch_next_record($rs)) {
  692. * /// Perform actions with the $rec record here
  693. * }
  694. * rs_close($rs); /// Close the recordset if not used anymore. Saves memory (optional but recommended).
  695. *
  696. * @param ADORecordSet the recordset to fetch current record from
  697. * @return mixed ADOFetchObj the object containing the fetched information or boolean false if no record (end of recordset)
  698. */
  699. function rs_fetch_next_record(&$rs) {
  700. global $CFG;
  701. if (!$rs) {
  702. debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
  703. return false;
  704. }
  705. $rec = false;
  706. $recarr = $rs->FetchRow(); //Retrieve record as object without advance the pointer. It's quicker that FetchNextObj()
  707. if ($recarr) {
  708. /// DIRTY HACK to retrieve all the ' ' (1 space) fields converted back
  709. /// to '' (empty string) for Oracle. It's the only way to work with
  710. /// all those NOT NULL DEFAULT '' fields until we definetively delete them
  711. if ($CFG->dbfamily == 'oracle') {
  712. array_walk($recarr, 'onespace2empty');
  713. }
  714. /// End DIRTY HACK
  715. /// Cast array to object
  716. $rec = (object)$recarr;
  717. }
  718. return $rec;
  719. }
  720. /**
  721. * Returns true if no more records found
  722. * @param ADORecordSet the recordset
  723. * @return bool
  724. */
  725. function rs_EOF($rs) {
  726. if (!$rs) {
  727. debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
  728. return true;
  729. }
  730. return $rs->EOF;
  731. }
  732. /**
  733. * This function closes the recordset, freeing all the memory and associated resources.
  734. * Note that, once closed, the recordset must not be used anymore along the request.
  735. * Saves memory (optional but recommended).
  736. * @param ADORecordSet the recordset to be closed
  737. * @return void
  738. */
  739. function rs_close(&$rs) {
  740. if (!$rs) {
  741. debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
  742. return;
  743. }
  744. $rs->Close();
  745. }
  746. /**
  747. * This function is used to convert all the Oracle 1-space defaults to the empty string
  748. * like a really DIRTY HACK to allow it to work better until all those NOT NULL DEFAULT ''
  749. * fields will be out from Moodle.
  750. * @param string the string to be converted to '' (empty string) if it's ' ' (one space)
  751. * @param mixed the key of the array in case we are using this function from array_walk,
  752. * defaults to null for other (direct) uses
  753. * @return boolean always true (the converted variable is returned by reference)
  754. */
  755. function onespace2empty(&$item, $key=null) {
  756. $item = $item == ' ' ? '' : $item;
  757. return true;
  758. }
  759. ///End DIRTY HACK
  760. /**
  761. * Get a number of records as an array of objects.
  762. *
  763. * If the query succeeds and returns at least one record, the
  764. * return value is an array of objects, one object for each
  765. * record found. The array key is the value from the first
  766. * column of the result set. The object associated with that key
  767. * has a member variable for each column of the results.
  768. *
  769. * @param string $table the table to query.
  770. * @param string $field a field to check (optional).
  771. * @param string $value the value the field must have (requred if field1 is given, else optional).
  772. * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
  773. * @param string $fields a comma separated list of fields to return (optional, by default
  774. * all fields are returned). The first field will be used as key for the
  775. * array so must be a unique field such as 'id'.
  776. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  777. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  778. * @return mixed an array of objects, or false if no records were found or an error occured.
  779. */
  780. function get_records($table, $field='', $value='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  781. $rs = get_recordset($table, $field, $value, $sort, $fields, $limitfrom, $limitnum);
  782. return recordset_to_array($rs);
  783. }
  784. /**
  785. * Get a number of records as an array of objects.
  786. *
  787. * Return value as for @see function get_records.
  788. *
  789. * @param string $table the table to query.
  790. * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
  791. * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
  792. * @param string $fields a comma separated list of fields to return
  793. * (optional, by default all fields are returned). The first field will be used as key for the
  794. * array so must be a unique field such as 'id'.
  795. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  796. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  797. * @return mixed an array of objects, or false if no records were found or an error occured.
  798. */
  799. function get_records_select($table, $select='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  800. $rs = get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
  801. return recordset_to_array($rs);
  802. }
  803. /**
  804. * Get a number of records as an array of objects.
  805. *
  806. * Return value as for @see function get_records.
  807. *
  808. * @param string $table The database table to be checked against.
  809. * @param string $field The field to search
  810. * @param string $values Comma separated list of possible value
  811. * @param string $sort Sort order (as valid SQL sort parameter)
  812. * @param string $fields A comma separated list of fields to be returned from the chosen table. If specified,
  813. * the first field should be a unique one such as 'id' since it will be used as a key in the associative
  814. * array.
  815. * @return mixed an array of objects, or false if no records were found or an error occured.
  816. */
  817. function get_records_list($table, $field='', $values='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  818. $rs = get_recordset_list($table, $field, $values, $sort, $fields, $limitfrom, $limitnum);
  819. return recordset_to_array($rs);
  820. }
  821. /**
  822. * Get a number of records as an array of objects.
  823. *
  824. * Return value as for @see function get_records.
  825. *
  826. * @param string $sql the SQL select query to execute. The first column of this SELECT statement
  827. * must be a unique value (usually the 'id' field), as it will be used as the key of the
  828. * returned array.
  829. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  830. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  831. * @return mixed an array of objects, or false if no records were found or an error occured.
  832. */
  833. function get_records_sql($sql, $limitfrom='', $limitnum='') {
  834. $rs = get_recordset_sql($sql, $limitfrom, $limitnum);
  835. return recordset_to_array($rs);
  836. }
  837. /**
  838. * Utility function used by the following 3 methods.
  839. *
  840. * @param object an ADODB RecordSet object with two columns.
  841. * @return mixed an associative array, or false if an error occured or the RecordSet was empty.
  842. */
  843. function recordset_to_menu($rs) {
  844. global $CFG;
  845. $menu = array();
  846. if ($rs && !rs_EOF($rs)) {
  847. $keys = array_keys($rs->fields);
  848. $key0=$keys[0];
  849. $key1=$keys[1];
  850. while (!$rs->EOF) {
  851. $menu[$rs->fields[$key0]] = $rs->fields[$key1];
  852. $rs->MoveNext();
  853. }
  854. /// Really DIRTY HACK for Oracle, but it's the only way to make it work
  855. /// until we got all those NOT NULL DEFAULT '' out from Moodle
  856. if ($CFG->dbfamily == 'oracle') {
  857. array_walk($menu, 'onespace2empty');
  858. }
  859. /// End of DIRTY HACK
  860. return $menu;
  861. } else {
  862. return false;
  863. }
  864. }
  865. /**
  866. * Utility function
  867. * Similar to recordset_to_menu
  868. *
  869. * field1, field2 is needed because the order from get_records_sql is not reliable
  870. * @param records - records from get_records_sql() or get_records()
  871. * @param field1 - field to be used as menu index
  872. * @param field2 - feild to be used as coresponding menu value
  873. * @return mixed an associative array, or false if an error occured or the RecordSet was empty.
  874. */
  875. function records_to_menu($records, $field1, $field2) {
  876. $menu = array();
  877. foreach ($records as $record) {
  878. $menu[$record->$field1] = $record->$field2;
  879. }
  880. if (!empty($menu)) {
  881. return $menu;
  882. } else {
  883. return false;
  884. }
  885. }
  886. /**
  887. * Get the first two columns from a number of records as an associative array.
  888. *
  889. * Arguments as for @see function get_recordset.
  890. *
  891. * If no errors occur, and at least one records is found, the return value
  892. * is an associative whose keys come from the first field of each record,
  893. * and whose values are the corresponding second fields. If no records are found,
  894. * or an error occurs, false is returned.
  895. *
  896. * @param string $table the table to query.
  897. * @param string $field a field to check (optional).
  898. * @param string $value the value the field must have (requred if field1 is given, else optional).
  899. * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
  900. * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
  901. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  902. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  903. * @return mixed an associative array, or false if no records were found or an error occured.
  904. */
  905. function get_records_menu($table, $field='', $value='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  906. $rs = get_recordset($table, $field, $value, $sort, $fields, $limitfrom, $limitnum);
  907. return recordset_to_menu($rs);
  908. }
  909. /**
  910. * Get the first two columns from a number of records as an associative array.
  911. *
  912. * Arguments as for @see function get_recordset_select.
  913. * Return value as for @see function get_records_menu.
  914. *
  915. * @param string $table The database table to be checked against.
  916. * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
  917. * @param string $sort Sort order (optional) - a valid SQL order parameter
  918. * @param string $fields A comma separated list of fields to be returned from the chosen table.
  919. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  920. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  921. * @return mixed an associative array, or false if no records were found or an error occured.
  922. */
  923. function get_records_select_menu($table, $select='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
  924. $rs = get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
  925. return recordset_to_menu($rs);
  926. }
  927. /**
  928. * Get the first two columns from a number of records as an associative array.
  929. *
  930. * Arguments as for @see function get_recordset_sql.
  931. * Return value as for @see function get_records_menu.
  932. *
  933. * @param string $sql The SQL string you wish to be executed.
  934. * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
  935. * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
  936. * @return mixed an associative array, or false if no records were found or an error occured.
  937. */
  938. function get_records_sql_menu($sql, $limitfrom='', $limitnum='') {
  939. $rs = get_recordset_sql($sql, $limitfrom, $limitnum);
  940. return recordset_to_menu($rs);
  941. }
  942. /**
  943. * Get a single value from a table row where all the given fields match the given values.
  944. *
  945. * @param string $table the table to query.
  946. * @param string $return the field to return the value of.
  947. * @param string $field1 the first field to check (optional).
  948. * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
  949. * @param string $field2 the second field to check (optional).
  950. * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
  951. * @param string $field3 the third field to check (optional).
  952. * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
  953. * @return mixed the specified value, or false if an error occured.
  954. */
  955. function get_field($table, $return, $field1, $value1, $field2='', $value2='', $field3='', $value3='') {
  956. global $CFG;
  957. $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
  958. return get_field_sql('SELECT ' . $return . ' FROM ' . $CFG->prefix . $table . ' ' . $select);
  959. }
  960. /**
  961. * Get a single value from a table row where a particular select clause is true.
  962. *
  963. * @uses $CFG
  964. * @param string $table the table to query.
  965. * @param string $return the field to return the value of.
  966. * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
  967. * @return mixed the specified value, or false if an error occured.
  968. */
  969. function get_field_select($table, $return, $select) {
  970. global $CFG;
  971. if ($select) {
  972. $select = 'WHERE '. $select;
  973. }
  974. return get_field_sql('SELECT ' . $return . ' FROM ' . $CFG->prefix . $table . ' ' . $select);
  975. }
  976. /**
  977. * Get a single value from a table.
  978. *
  979. * @param string $sql an SQL statement expected to return a single value.
  980. * @return mixed the specified value, or false if an error occured.
  981. */
  982. function get_field_sql($sql) {
  983. global $CFG;
  984. /// Strip potential LIMIT uses arriving here, debugging them (MDL-7173)
  985. $newsql = preg_replace('/ LIMIT [0-9, ]+$/is', '', $sql);
  986. if ($newsql != $sql) {
  987. debugging('Incorrect use of LIMIT clause (not cross-db) in call to get_field_sql(): ' . s($sql), DEBUG_DEVELOPER);
  988. $sql = $newsql;
  989. }
  990. $rs = get_recordset_sql($sql, 0, 1);
  991. if ($rs && $rs->RecordCount() == 1) {
  992. /// DIRTY HACK to retrieve all the ' ' (1 space) fields converted back
  993. /// to '' (empty string) for Oracle. It's the only way to work with
  994. /// all those NOT NULL DEFAULT '' fields until we definetively delete them
  995. if ($CFG->dbfamily == 'oracle') {
  996. $value = reset($rs->fields);
  997. onespace2empty($value);
  998. return $value;
  999. }
  1000. /// End of DIRTY HACK
  1001. return reset($rs->fields);
  1002. } else {
  1003. return false;
  1004. }
  1005. }
  1006. /**
  1007. * Get a single value from a table row where a particular select clause is true.
  1008. *
  1009. * @uses $CFG
  1010. * @param string $table the table to query.
  1011. * @param string $return the field to return the value of.
  1012. * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
  1013. * @return mixed|false Returns the value return from the SQL statment or false if an error occured.
  1014. */
  1015. function get_fieldset_select($table, $return, $select) {
  1016. global $CFG;
  1017. if ($select) {
  1018. $select = ' WHERE '. $select;
  1019. }
  1020. return get_fieldset_sql('SELECT ' . $return . ' FROM ' . $CFG->prefix . $table . $select);
  1021. }
  1022. /**
  1023. * Get an array of data from one or more fields from a database
  1024. * use to get a column, or a series of distinct values
  1025. *
  1026. * @uses $CFG
  1027. * @uses $db
  1028. * @param string $sql The SQL string you wish to be executed.
  1029. * @return mixed|false Returns the value return from the SQL statment or false if an error occured.
  1030. * @todo Finish documenting this function
  1031. */
  1032. function get_fieldset_sql($sql) {
  1033. global $db, $CFG;
  1034. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  1035. $rs = $db->Execute($sql);
  1036. if (!$rs) {
  1037. debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
  1038. if (!empty($CFG->dblogerror)) {
  1039. $debug=array_shift(debug_backtrace());
  1040. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
  1041. }
  1042. return false;
  1043. }
  1044. if ( !rs_EOF($rs) ) {
  1045. $keys = array_keys($rs->fields);
  1046. $key0 = $keys[0];
  1047. $results = array();
  1048. while (!$rs->EOF) {
  1049. array_push($results, $rs->fields[$key0]);
  1050. $rs->MoveNext();
  1051. }
  1052. /// DIRTY HACK to retrieve all the ' ' (1 space) fields converted back
  1053. /// to '' (empty string) for Oracle. It's the only way to work with
  1054. /// all those NOT NULL DEFAULT '' fields until we definetively delete them
  1055. if ($CFG->dbfamily == 'oracle') {
  1056. array_walk($results, 'onespace2empty');
  1057. }
  1058. /// End of DIRTY HACK
  1059. rs_close($rs);
  1060. return $results;
  1061. } else {
  1062. rs_close($rs);
  1063. return false;
  1064. }
  1065. }
  1066. /**
  1067. * Set a single field in every table row where all the given fields match the given values.
  1068. *
  1069. * @uses $CFG
  1070. * @uses $db
  1071. * @param string $table The database table to be checked against.
  1072. * @param string $newfield the field to set.
  1073. * @param string $newvalue the value to set the field to.
  1074. * @param string $field1 the first field to check (optional).
  1075. * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
  1076. * @param string $field2 the second field to check (optional).
  1077. * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
  1078. * @param string $field3 the third field to check (optional).
  1079. * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
  1080. * @return mixed An ADODB RecordSet object with the results from the SQL call or false.
  1081. */
  1082. function set_field($table, $newfield, $newvalue, $field1, $value1, $field2='', $value2='', $field3='', $value3='') {
  1083. global $CFG;
  1084. // Clear record_cache based on the parameters passed
  1085. // (individual record or whole table)
  1086. if ($CFG->rcache === true) {
  1087. if ($field1 == 'id') {
  1088. rcache_unset($table, $value1);
  1089. } else if ($field2 == 'id') {
  1090. rcache_unset($table, $value2);
  1091. } else if ($field3 == 'id') {
  1092. rcache_unset($table, $value3);
  1093. } else {
  1094. rcache_unset_table($table);
  1095. }
  1096. }
  1097. $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
  1098. return set_field_select($table, $newfield, $newvalue, $select, true);
  1099. }
  1100. /**
  1101. * Set a single field in every table row where the select statement evaluates to true.
  1102. *
  1103. * @uses $CFG
  1104. * @uses $db
  1105. * @param string $table The database table to be checked against.
  1106. * @param string $newfield the field to set.
  1107. * @param string $newvalue the value to set the field to.
  1108. * @param string $select a fragment of SQL to be used in a where clause in the SQL call.
  1109. * @param boolean $localcall Leave this set to false. (Should only be set to true by set_field.)
  1110. * @return mixed An ADODB RecordSet object with the results from the SQL call or false.
  1111. */
  1112. function set_field_select($table, $newfield, $newvalue, $select, $localcall = false) {
  1113. global $db, $CFG;
  1114. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  1115. if (!$localcall) {
  1116. if ($select) {
  1117. $select = 'WHERE ' . $select;
  1118. }
  1119. // Clear record_cache based on the parameters passed
  1120. // (individual record or whole table)
  1121. if ($CFG->rcache === true) {
  1122. rcache_unset_table($table);
  1123. }
  1124. }
  1125. $dataobject = new StdClass;
  1126. $dataobject->{$newfield} = $newvalue;
  1127. // Oracle DIRTY HACK -
  1128. if ($CFG->dbfamily == 'oracle') {
  1129. oracle_dirty_hack($table, $dataobject); // Convert object to the correct "empty" values for Oracle DB
  1130. $newvalue = $dataobject->{$newfield};
  1131. }
  1132. // End DIRTY HACK
  1133. /// Under Oracle, MSSQL and PostgreSQL we have our own set field process
  1134. /// If the field being updated is clob/blob, we use our alternate update here
  1135. /// They will be updated later
  1136. if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') && !empty($select)) {
  1137. /// Detect lobs
  1138. $foundclobs = array();
  1139. $foundblobs = array();
  1140. db_detect_lobs($table, $dataobject, $foundclobs, $foundblobs);
  1141. }
  1142. /// Under Oracle, MSSQL and PostgreSQL, finally, update all the Clobs and Blobs present in the record
  1143. /// if we know we have some of them in the query
  1144. if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') && !empty($select) &&
  1145. (!empty($foundclobs) || !empty($foundblobs))) {
  1146. if (!db_update_lobs($table, $select, $foundclobs, $foundblobs)) {
  1147. return false; //Some error happened while updating LOBs
  1148. } else {
  1149. return true; //Everrything was ok
  1150. }
  1151. }
  1152. /// NULL inserts - introduced in 1.9
  1153. if (is_null($newvalue)) {
  1154. $update = "$newfield = NULL";
  1155. } else {
  1156. $update = "$newfield = '$newvalue'";
  1157. }
  1158. /// Arriving here, standard update
  1159. $sql = 'UPDATE '. $CFG->prefix . $table .' SET '.$update.' '.$select;
  1160. $rs = $db->Execute($sql);
  1161. if (!$rs) {
  1162. debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
  1163. if (!empty($CFG->dblogerror)) {
  1164. $debug=array_shift(debug_backtrace());
  1165. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
  1166. }
  1167. return false;
  1168. }
  1169. return $rs;
  1170. }
  1171. /**
  1172. * Delete the records from a table where all the given fields match the given values.
  1173. *
  1174. * @uses $CFG
  1175. * @uses $db
  1176. * @param string $table the table to delete from.
  1177. * @param string $field1 the first field to check (optional).
  1178. * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
  1179. * @param string $field2 the second field to check (optional).
  1180. * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
  1181. * @param string $field3 the third field to check (optional).
  1182. * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
  1183. * @return mixed An ADODB RecordSet object with the results from the SQL call or false.
  1184. */
  1185. function delete_records($table, $field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
  1186. global $db, $CFG;
  1187. // Clear record_cache based on the parameters passed
  1188. // (individual record or whole table)
  1189. if ($CFG->rcache === true) {
  1190. if ($field1 == 'id') {
  1191. rcache_unset($table, $value1);
  1192. } else if ($field2 == 'id') {
  1193. rcache_unset($table, $value2);
  1194. } else if ($field3 == 'id') {
  1195. rcache_unset($table, $value3);
  1196. } else {
  1197. rcache_unset_table($table);
  1198. }
  1199. }
  1200. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  1201. $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
  1202. $sql = 'DELETE FROM '. $CFG->prefix . $table .' '. $select;
  1203. $rs = $db->Execute($sql);
  1204. if (!$rs) {
  1205. debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
  1206. if (!empty($CFG->dblogerror)) {
  1207. $debug=array_shift(debug_backtrace());
  1208. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
  1209. }
  1210. return false;
  1211. }
  1212. return $rs;
  1213. }
  1214. /**
  1215. * Delete one or more records from a table
  1216. *
  1217. * @uses $CFG
  1218. * @uses $db
  1219. * @param string $table The database table to be checked against.
  1220. * @param string $select A fragment of SQL to be used in a where clause in the SQL call (used to define the selection criteria).
  1221. * @return object A PHP standard object with the results from the SQL call.
  1222. * @todo Verify return type.
  1223. */
  1224. function delete_records_select($table, $select='') {
  1225. global $CFG, $db;
  1226. // Clear record_cache (whole table)
  1227. if ($CFG->rcache === true) {
  1228. rcache_unset_table($table);
  1229. }
  1230. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  1231. if ($select) {
  1232. $select = 'WHERE '.$select;
  1233. }
  1234. $sql = 'DELETE FROM '. $CFG->prefix . $table .' '. $select;
  1235. $rs = $db->Execute($sql);
  1236. if (!$rs) {
  1237. debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
  1238. if (!empty($CFG->dblogerror)) {
  1239. $debug=array_shift(debug_backtrace());
  1240. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
  1241. }
  1242. return false;
  1243. }
  1244. return $rs;
  1245. }
  1246. /**
  1247. * Insert a record into a table and return the "id" field if required
  1248. *
  1249. * If the return ID isn't required, then this just reports success as true/false.
  1250. * $dataobject is an object containing needed data
  1251. *
  1252. * @uses $db
  1253. * @uses $CFG
  1254. * @param string $table The database table to be checked against.
  1255. * @param object $dataobject A data object with values for one or more fields in the record
  1256. * @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.
  1257. * @param string $primarykey (obsolete) This is now forced to be 'id'.
  1258. */
  1259. function insert_record($table, $dataobject, $returnid=true, $primarykey='id') {
  1260. global $db, $CFG, $empty_rs_cache;
  1261. if (empty($db)) {
  1262. return false;
  1263. }
  1264. /// Check we are handling a proper $dataobject
  1265. if (is_array($dataobject)) {
  1266. debugging('Warning. Wrong call to insert_record(). $dataobject must be an object. array found instead', DEBUG_DEVELOPER);
  1267. $dataobject = (object)$dataobject;
  1268. }
  1269. /// Temporary hack as part of phasing out all access to obsolete user tables XXX
  1270. if (!empty($CFG->rolesactive)) {
  1271. if (in_array($table, array('user_students', 'user_teachers', 'user_coursecreators', 'user_admins'))) {
  1272. if (debugging()) { var_dump(debug_backtrace()); }
  1273. error('This SQL relies on obsolete tables ('.$table.')! Your code must be fixed by a developer.');
  1274. }
  1275. }
  1276. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  1277. /// In Moodle we always use auto-numbering fields for the primary key
  1278. /// so let's unset it now before it causes any trouble later
  1279. unset($dataobject->{$primarykey});
  1280. /// Get an empty recordset. Cache for multiple inserts.
  1281. if (empty($empty_rs_cache[$table])) {
  1282. /// Execute a dummy query to get an empty recordset
  1283. if (!$empty_rs_cache[$table] = $db->Execute('SELECT * FROM '. $CFG->prefix . $table .' WHERE '. $primarykey .' = \'-1\'')) {
  1284. return false;
  1285. }
  1286. }
  1287. $rs = $empty_rs_cache[$table];
  1288. /// Postgres doesn't have the concept of primary key built in
  1289. /// and will return the OID which isn't what we want.
  1290. /// The efficient and transaction-safe strategy is to
  1291. /// move the sequence forward first, and make the insert
  1292. /// with an explicit id.
  1293. if ( $CFG->dbfamily === 'postgres' && $returnid == true ) {
  1294. if ($nextval = (int)get_field_sql("SELECT NEXTVAL('{$CFG->prefix}{$table}_{$primarykey}_seq')")) {
  1295. $dataobject->{$primarykey} = $nextval;
  1296. }
  1297. }
  1298. /// Begin DIRTY HACK
  1299. if ($CFG->dbfamily == 'oracle') {
  1300. oracle_dirty_hack($table, $dataobject); // Convert object to the correct "empty" values for Oracle DB
  1301. }
  1302. /// End DIRTY HACK
  1303. /// Under Oracle, MSSQL and PostgreSQL we have our own insert record process
  1304. /// detect all the clob/blob fields and change their contents to @#CLOB#@ and @#BLOB#@
  1305. /// saving them into $foundclobs and $foundblobs [$fieldname]->contents
  1306. /// Same for mssql (only processing blobs - image fields)
  1307. if ($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') {
  1308. $foundclobs = array();
  1309. $foundblobs = array();
  1310. db_detect_lobs($table, $dataobject, $foundclobs, $foundblobs);
  1311. }
  1312. /// Under Oracle, if the primary key inserted has been requested OR
  1313. /// if there are LOBs to insert, we calculate the next value via
  1314. /// explicit query to the sequence.
  1315. /// Else, the pre-insert trigger will do the job, because the primary
  1316. /// key isn't needed at all by the rest of PHP code
  1317. if ($CFG->dbfamily === 'oracle' && ($returnid == true || !empty($foundclobs) || !empty($foundblobs))) {
  1318. /// We need this here (move this function to dmlib?)
  1319. include_once($CFG->libdir . '/ddllib.php');
  1320. $xmldb_table = new XMLDBTable($table);
  1321. $seqname = find_sequence_name($xmldb_table);
  1322. if (!$seqname) {
  1323. /// Fallback, seqname not found, something is wrong. Inform and use the alternative getNameForObject() method
  1324. debugging('Sequence name for table ' . $table->getName() . ' not found', DEBUG_DEVELOPER);
  1325. $generator = new XMLDBoci8po();
  1326. $generator->setPrefix($CFG->prefix);
  1327. $seqname = $generator->getNameForObject($table, $primarykey, 'seq');
  1328. }
  1329. if ($nextval = (int)$db->GenID($seqname)) {
  1330. $dataobject->{$primarykey} = $nextval;
  1331. } else {
  1332. debugging('Not able to get value from sequence ' . $seqname, DEBUG_DEVELOPER);
  1333. }
  1334. }
  1335. /// Get the correct SQL from adoDB
  1336. if (!$insertSQL = $db->GetInsertSQL($rs, (array)$dataobject, true)) {
  1337. return false;
  1338. }
  1339. /// Under Oracle, MSSQL and PostgreSQL, replace all the '@#CLOB#@' and '@#BLOB#@' ocurrences to proper default values
  1340. /// if we know we have some of them in the query
  1341. if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') &&
  1342. (!empty($foundclobs) || !empty($foundblobs))) {
  1343. /// Initial configuration, based on DB
  1344. switch ($CFG->dbfamily) {
  1345. case 'oracle':
  1346. $clobdefault = 'empty_clob()'; //Value of empty default clobs for this DB
  1347. $blobdefault = 'empty_blob()'; //Value of empty default blobs for this DB
  1348. break;
  1349. case 'mssql':
  1350. case 'postgres':
  1351. $clobdefault = 'null'; //Value of empty default clobs for this DB (under mssql this won't be executed
  1352. $blobdefault = 'null'; //Value of empty default blobs for this DB
  1353. break;
  1354. }
  1355. $insertSQL = str_replace("'@#CLOB#@'", $clobdefault, $insertSQL);
  1356. $insertSQL = str_replace("'@#BLOB#@'", $blobdefault, $insertSQL);
  1357. }
  1358. /// Run the SQL statement
  1359. if (!$rs = $db->Execute($insertSQL)) {
  1360. debugging($db->ErrorMsg() .'<br /><br />'.s($insertSQL));
  1361. if (!empty($CFG->dblogerror)) {
  1362. $debug=array_shift(debug_backtrace());
  1363. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $insertSQL");
  1364. }
  1365. return false;
  1366. }
  1367. /// Under Oracle and PostgreSQL, finally, update all the Clobs and Blobs present in the record
  1368. /// if we know we have some of them in the query
  1369. if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'postgres') &&
  1370. !empty($dataobject->{$primarykey}) &&
  1371. (!empty($foundclobs) || !empty($foundblobs))) {
  1372. if (!db_update_lobs($table, $dataobject->{$primarykey}, $foundclobs, $foundblobs)) {
  1373. return false; //Some error happened while updating LOBs
  1374. }
  1375. }
  1376. /// If a return ID is not needed then just return true now (but not in MSSQL DBs, where we may have some pending tasks)
  1377. if (!$returnid && $CFG->dbfamily != 'mssql') {
  1378. return true;
  1379. }
  1380. /// We already know the record PK if it's been passed explicitly,
  1381. /// or if we've retrieved it from a sequence (Postgres and Oracle).
  1382. if (!empty($dataobject->{$primarykey})) {
  1383. return $dataobject->{$primarykey};
  1384. }
  1385. /// This only gets triggered with MySQL and MSQL databases
  1386. /// however we have some postgres fallback in case we failed
  1387. /// to find the sequence.
  1388. $id = $db->Insert_ID();
  1389. /// Under MSSQL all the Clobs and Blobs (IMAGE) present in the record
  1390. /// if we know we have some of them in the query
  1391. if (($CFG->dbfamily == 'mssql') &&
  1392. !empty($id) &&
  1393. (!empty($foundclobs) || !empty($foundblobs))) {
  1394. if (!db_update_lobs($table, $id, $foundclobs, $foundblobs)) {
  1395. return false; //Some error happened while updating LOBs
  1396. }
  1397. }
  1398. if ($CFG->dbfamily === 'postgres') {
  1399. // try to get the primary key based on id
  1400. if ( ($rs = $db->Execute('SELECT '. $primarykey .' FROM '. $CFG->prefix . $table .' WHERE oid = '. $id))
  1401. && ($rs->RecordCount() == 1) ) {
  1402. trigger_error("Retrieved $primarykey from oid on table $table because we could not find the sequence.");
  1403. return (integer)reset($rs->fields);
  1404. }
  1405. trigger_error('Failed to retrieve primary key after insert: SELECT '. $primarykey .
  1406. ' FROM '. $CFG->prefix . $table .' WHERE oid = '. $id);
  1407. return false;
  1408. }
  1409. return (integer)$id;
  1410. }
  1411. /**
  1412. * Update a record in a table
  1413. *
  1414. * $dataobject is an object containing needed data
  1415. * Relies on $dataobject having a variable "id" to
  1416. * specify the record to update
  1417. *
  1418. * @uses $CFG
  1419. * @uses $db
  1420. * @param string $table The database table to be checked against.
  1421. * @param object $dataobject An object with contents equal to fieldname=>fieldvalue. Must have an entry for 'id' to map to the table specified.
  1422. * @return bool
  1423. */
  1424. function update_record($table, $dataobject) {
  1425. global $db, $CFG;
  1426. // integer value in id propery required
  1427. if (empty($dataobject->id)) {
  1428. return false;
  1429. }
  1430. $dataobject->id = (int)$dataobject->id;
  1431. /// Check we are handling a proper $dataobject
  1432. if (is_array($dataobject)) {
  1433. debugging('Warning. Wrong call to update_record(). $dataobject must be an object. array found instead', DEBUG_DEVELOPER);
  1434. $dataobject = (object)$dataobject;
  1435. }
  1436. // Remove this record from record cache since it will change
  1437. if (!empty($CFG->rcache)) { // no === here! breaks upgrade
  1438. rcache_unset($table, $dataobject->id);
  1439. }
  1440. /// Temporary hack as part of phasing out all access to obsolete user tables XXX
  1441. if (!empty($CFG->rolesactive)) {
  1442. if (in_array($table, array('user_students', 'user_teachers', 'user_coursecreators', 'user_admins'))) {
  1443. if (debugging()) { var_dump(debug_backtrace()); }
  1444. error('This SQL relies on obsolete tables ('.$table.')! Your code must be fixed by a developer.');
  1445. }
  1446. }
  1447. /// Begin DIRTY HACK
  1448. if ($CFG->dbfamily == 'oracle') {
  1449. oracle_dirty_hack($table, $dataobject); // Convert object to the correct "empty" values for Oracle DB
  1450. }
  1451. /// End DIRTY HACK
  1452. /// Under Oracle, MSSQL and PostgreSQL we have our own update record process
  1453. /// detect all the clob/blob fields and delete them from the record being updated
  1454. /// saving them into $foundclobs and $foundblobs [$fieldname]->contents
  1455. /// They will be updated later
  1456. if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres' || $CFG->dbfamily == 'db2')
  1457. && !empty($dataobject->id)) {
  1458. /// Detect lobs
  1459. $foundclobs = array();
  1460. $foundblobs = array();
  1461. db_detect_lobs($table, $dataobject, $foundclobs, $foundblobs, true);
  1462. }
  1463. // Determine all the fields in the table
  1464. if (!$columns = $db->MetaColumns($CFG->prefix . $table)) {
  1465. return false;
  1466. }
  1467. $data = (array)$dataobject;
  1468. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  1469. // Pull out data matching these fields
  1470. $update = array();
  1471. if($CFG->dbfamily == 'db2'){
  1472. foreach ($columns as $column) {
  1473. $db2_colname = strtolower($column->name);
  1474. if ($db2_colname == 'id') {
  1475. continue;
  1476. }
  1477. if (array_key_exists($db2_colname, $data)) {
  1478. $key = $db2_colname;
  1479. $value = $data[$key];
  1480. if (is_null($value)) {
  1481. $update[] = "$key = NULL"; // previously NULLs were not updated
  1482. } else if (is_bool($value)) {
  1483. $value = (int)$value;
  1484. $update[] = "$key = $value"; // lets keep pg happy, '' is not correct smallint MDL-13038
  1485. } else {
  1486. $update[] = "$key = '$value'"; // All incoming data is already quoted
  1487. }
  1488. }
  1489. }
  1490. } else {
  1491. foreach ($columns as $column) {
  1492. if ($column->name == 'id') {
  1493. continue;
  1494. }
  1495. if (array_key_exists($column->name, $data)) {
  1496. $key = $column->name;
  1497. $value = $data[$key];
  1498. if (is_null($value)) {
  1499. $update[] = "$key = NULL"; // previously NULLs were not updated
  1500. } else if (is_bool($value)) {
  1501. $value = (int)$value;
  1502. $update[] = "$key = $value"; // lets keep pg happy, '' is not correct smallint MDL-13038
  1503. } else {
  1504. $update[] = "$key = '$value'"; // All incoming data is already quoted
  1505. }
  1506. }
  1507. }
  1508. }
  1509. /// Only if we have fields to be updated (this will prevent both wrong updates +
  1510. /// updates of only LOBs in Oracle
  1511. if ($update) {
  1512. $query = "UPDATE {$CFG->prefix}{$table} SET ".implode(',', $update)." WHERE id = {$dataobject->id}";
  1513. if (!$rs = $db->Execute($query)) {
  1514. debugging($db->ErrorMsg() .'<br /><br />'.s($query));
  1515. if (!empty($CFG->dblogerror)) {
  1516. $debug=array_shift(debug_backtrace());
  1517. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $query");
  1518. }
  1519. return false;
  1520. }
  1521. }
  1522. /// Under Oracle, MSSQL and PostgreSQL, finally, update all the Clobs and Blobs present in the record
  1523. /// if we know we have some of them in the query
  1524. if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres' || $CFG->dbfamily == 'db2') &&
  1525. !empty($dataobject->id) &&
  1526. (!empty($foundclobs) || !empty($foundblobs))) {
  1527. if (!db_update_lobs($table, $dataobject->id, $foundclobs, $foundblobs)) {
  1528. return false; //Some error happened while updating LOBs
  1529. }
  1530. }
  1531. return true;
  1532. }
  1533. /**
  1534. * Returns the proper SQL to do paging
  1535. *
  1536. * @uses $CFG
  1537. * @param string $page Offset page number
  1538. * @param string $recordsperpage Number of records per page
  1539. * @deprecated Moodle 1.7 use the new $limitfrom, $limitnum available in all
  1540. * the get_recordXXX() funcions.
  1541. * @return string
  1542. */
  1543. function sql_paging_limit($page, $recordsperpage) {
  1544. global $CFG;
  1545. debugging('Function sql_paging_limit() is deprecated. Replace it with the correct use of limitfrom, limitnum parameters', DEBUG_DEVELOPER);
  1546. switch ($CFG->dbfamily) {
  1547. case 'postgres':
  1548. return 'LIMIT '. $recordsperpage .' OFFSET '. $page;
  1549. default:
  1550. return 'LIMIT '. $page .','. $recordsperpage;
  1551. }
  1552. }
  1553. /**
  1554. * Returns the proper SQL to do LIKE in a case-insensitive way
  1555. *
  1556. * Note the LIKE are case sensitive for Oracle. Oracle 10g is required to use
  1557. * the caseinsensitive search using regexp_like() or NLS_COMP=LINGUISTIC :-(
  1558. * See http://docs.moodle.org/en/XMLDB_Problems#Case-insensitive_searches
  1559. *
  1560. * @uses $CFG
  1561. * @return string
  1562. */
  1563. function sql_ilike() {
  1564. global $CFG;
  1565. switch ($CFG->dbfamily) {
  1566. case 'postgres':
  1567. return 'ILIKE';
  1568. default:
  1569. return 'LIKE';
  1570. }
  1571. }
  1572. /**
  1573. * Returns the proper SQL to do MAX
  1574. *
  1575. * @uses $CFG
  1576. * @param string $field
  1577. * @return string
  1578. */
  1579. function sql_max($field) {
  1580. global $CFG;
  1581. switch ($CFG->dbfamily) {
  1582. default:
  1583. return "MAX($field)";
  1584. }
  1585. }
  1586. /**
  1587. * Returns the proper SQL (for the dbms in use) to concatenate $firstname and $lastname
  1588. *
  1589. * @uses $CFG
  1590. * @param string $firstname User's first name
  1591. * @param string $lastname User's last name
  1592. * @return string
  1593. */
  1594. function sql_fullname($firstname='firstname', $lastname='lastname') {
  1595. return sql_concat($firstname, "' '", $lastname);
  1596. }
  1597. /**
  1598. * Returns the proper SQL to do CONCAT between the elements passed
  1599. * Can take many parameters - just a passthrough to $db->Concat()
  1600. *
  1601. * @uses $db
  1602. * @param string $element
  1603. * @return string
  1604. */
  1605. function sql_concat() {
  1606. global $db, $CFG;
  1607. $args = func_get_args();
  1608. /// PostgreSQL requires at least one char element in the concat, let's add it
  1609. /// here (at the beginning of the array) until ADOdb fixes it
  1610. if ($CFG->dbfamily == 'postgres' && is_array($args)) {
  1611. array_unshift($args , "''");
  1612. }
  1613. return call_user_func_array(array($db, 'Concat'), $args);
  1614. }
  1615. /**
  1616. * Returns the proper SQL to do CONCAT between the elements passed
  1617. * with a given separator
  1618. *
  1619. * @uses $db
  1620. * @param string $separator
  1621. * @param array $elements
  1622. * @return string
  1623. */
  1624. function sql_concat_join($separator="' '", $elements=array()) {
  1625. global $db;
  1626. // copy to ensure pass by value
  1627. $elem = $elements;
  1628. // Intersperse $elements in the array.
  1629. // Add items to the array on the fly, walking it
  1630. // _backwards_ splicing the elements in. The loop definition
  1631. // should skip first and last positions.
  1632. for ($n=count($elem)-1; $n > 0 ; $n--) {
  1633. array_splice($elem, $n, 0, $separator);
  1634. }
  1635. return call_user_func_array(array($db, 'Concat'), $elem);
  1636. }
  1637. /**
  1638. * Returns the proper SQL to know if one field is empty.
  1639. *
  1640. * Note that the function behavior strongly relies on the
  1641. * parameters passed describing the field so, please, be accurate
  1642. * when speciffying them.
  1643. *
  1644. * Also, note that this function is not suitable to look for
  1645. * fields having NULL contents at all. It's all for empty values!
  1646. *
  1647. * This function should be applied in all the places where conditins of
  1648. * the type:
  1649. *
  1650. * ... AND fieldname = '';
  1651. *
  1652. * are being used. Final result should be:
  1653. *
  1654. * ... AND ' . sql_isempty('tablename', 'fieldname', true/false, true/false);
  1655. *
  1656. * (see parameters description below)
  1657. *
  1658. * @param string $tablename name of the table (without prefix). Not used for now but can be
  1659. * necessary in the future if we want to use some introspection using
  1660. * meta information against the DB. /// TODO ///
  1661. * @param string $fieldname name of the field we are going to check
  1662. * @param boolean $nullablefield to specify if the field us nullable (true) or no (false) in the DB
  1663. * @param boolean $textfield to specify if it is a text (also called clob) field (true) or a varchar one (false)
  1664. * @return string the sql code to be added to check for empty values
  1665. */
  1666. function sql_isempty($tablename, $fieldname, $nullablefield, $textfield) {
  1667. global $CFG;
  1668. $sql = $fieldname . " = ''";
  1669. switch ($CFG->dbfamily) {
  1670. case 'mssql':
  1671. if ($textfield) {
  1672. $sql = sql_compare_text($fieldname) . " = ''";
  1673. }
  1674. break;
  1675. case 'oracle':
  1676. if ($nullablefield) {
  1677. $sql = $fieldname . " IS NULL"; /// empties in nullable fields are stored as
  1678. } else { /// NULLs
  1679. if ($textfield) {
  1680. $sql = sql_compare_text($fieldname) . " = ' '"; /// oracle_dirty_hack inserts 1-whitespace
  1681. } else { /// in NOT NULL varchar and text columns so
  1682. $sql = $fieldname . " = ' '"; /// we need to look for that in any situation
  1683. }
  1684. }
  1685. break;
  1686. }
  1687. return ' ' . $sql . ' '; /// Adding spaces to avoid wrong SQLs due to concatenation
  1688. }
  1689. /**
  1690. * Returns the proper SQL to know if one field is not empty.
  1691. *
  1692. * Note that the function behavior strongly relies on the
  1693. * parameters passed describing the field so, please, be accurate
  1694. * when speciffying them.
  1695. *
  1696. * This function should be applied in all the places where conditions of
  1697. * the type:
  1698. *
  1699. * ... AND fieldname != '';
  1700. *
  1701. * are being used. Final result should be:
  1702. *
  1703. * ... AND ' . sql_isnotempty('tablename', 'fieldname', true/false, true/false);
  1704. *
  1705. * (see parameters description below)
  1706. *
  1707. * @param string $tablename name of the table (without prefix). Not used for now but can be
  1708. * necessary in the future if we want to use some introspection using
  1709. * meta information against the DB. /// TODO ///
  1710. * @param string $fieldname name of the field we are going to check
  1711. * @param boolean $nullablefield to specify if the field us nullable (true) or no (false) in the DB
  1712. * @param boolean $textfield to specify if it is a text (also called clob) field (true) or a varchar one (false)
  1713. * @return string the sql code to be added to check for non empty values
  1714. */
  1715. function sql_isnotempty($tablename, $fieldname, $nullablefield, $textfield) {
  1716. return ' ( NOT ' . sql_isempty($tablename, $fieldname, $nullablefield, $textfield) . ') ';
  1717. }
  1718. /**
  1719. * Returns the proper AS keyword to be used to aliase columns
  1720. * SQL defines the keyword as optional and nobody but PG
  1721. * seems to require it. This function should be used inside all
  1722. * the statements using column aliases.
  1723. * Note than the use of table aliases doesn't require the
  1724. * AS keyword at all, only columns for postgres.
  1725. * @uses $CFG
  1726. * @ return string the keyword
  1727. * @deprecated Moodle 1.7 because coding guidelines now enforce to use AS in column aliases
  1728. */
  1729. function sql_as() {
  1730. global $CFG, $db;
  1731. switch ($CFG->dbfamily) {
  1732. case 'postgres':
  1733. return 'AS';
  1734. default:
  1735. return '';
  1736. }
  1737. }
  1738. /**
  1739. * Returns the empty string char used by every supported DB. To be used when
  1740. * we are searching for that values in our queries. Only Oracle uses this
  1741. * for now (will be out, once we migrate to proper NULLs if that days arrives)
  1742. */
  1743. function sql_empty() {
  1744. global $CFG;
  1745. switch ($CFG->dbfamily) {
  1746. case 'oracle':
  1747. return ' '; //Only Oracle uses 1 white-space
  1748. default:
  1749. return '';
  1750. }
  1751. }
  1752. /**
  1753. * Returns the proper substr() function for each DB
  1754. * Relies on ADOdb $db->substr property
  1755. */
  1756. function sql_substr() {
  1757. global $db;
  1758. return $db->substr;
  1759. }
  1760. /**
  1761. * Returns the SQL text to be used to compare one TEXT (clob) column with
  1762. * one varchar column, because some RDBMS doesn't support such direct
  1763. * comparisons.
  1764. * @param string fieldname the name of the TEXT field we need to order by
  1765. * @param string number of chars to use for the ordering (defaults to 32)
  1766. * @return string the piece of SQL code to be used in your statement.
  1767. */
  1768. function sql_compare_text($fieldname, $numchars=32) {
  1769. return sql_order_by_text($fieldname, $numchars);
  1770. }
  1771. /**
  1772. * Returns the SQL text to be used to order by one TEXT (clob) column, because
  1773. * some RDBMS doesn't support direct ordering of such fields.
  1774. * Note that the use or queries being ordered by TEXT columns must be minimised,
  1775. * because it's really slooooooow.
  1776. * @param string fieldname the name of the TEXT field we need to order by
  1777. * @param string number of chars to use for the ordering (defaults to 32)
  1778. * @return string the piece of SQL code to be used in your statement.
  1779. */
  1780. function sql_order_by_text($fieldname, $numchars=32) {
  1781. global $CFG;
  1782. switch ($CFG->dbfamily) {
  1783. case 'mssql':
  1784. return 'CONVERT(varchar, ' . $fieldname . ', ' . $numchars . ')';
  1785. break;
  1786. case 'oracle':
  1787. return 'dbms_lob.substr(' . $fieldname . ', ' . $numchars . ',1)';
  1788. break;
  1789. default:
  1790. return $fieldname;
  1791. }
  1792. }
  1793. /**
  1794. * Returns the SQL text to be used to calculate the length in characters of one expression.
  1795. * @param string fieldname or expression to calculate its length in characters.
  1796. * @return string the piece of SQL code to be used in the statement.
  1797. */
  1798. function sql_length($fieldname) {
  1799. global $CFG;
  1800. switch ($CFG->dbfamily) {
  1801. case 'mysql':
  1802. return 'CHAR_LENGTH(' . $fieldname . ')';
  1803. break;
  1804. case 'mssql':
  1805. return 'LEN(' . $fieldname . ')';
  1806. break;
  1807. default:
  1808. return 'LENGTH(' . $fieldname . ')';
  1809. }
  1810. }
  1811. /**
  1812. * Returns the SQL for returning searching one string for the location of another.
  1813. * @param string $needle the SQL expression that will be searched for.
  1814. * @param string $haystack the SQL expression that will be searched in.
  1815. * @return string the required SQL
  1816. */
  1817. function sql_position($needle, $haystack) {
  1818. global $CFG;
  1819. switch ($CFG->dbfamily) {
  1820. case 'mssql':
  1821. return "CHARINDEX(($needle), ($haystack))";
  1822. break;
  1823. case 'oracle':
  1824. return "INSTR(($haystack), ($needle))";
  1825. break;
  1826. default:
  1827. return "POSITION(($needle) IN ($haystack))";
  1828. }
  1829. }
  1830. /**
  1831. * Returns the SQL to be used in order to CAST one CHAR column to INTEGER.
  1832. *
  1833. * Be aware that the CHAR column you're trying to cast contains really
  1834. * int values or the RDBMS will throw an error!
  1835. *
  1836. * @param string fieldname the name of the field to be casted
  1837. * @param boolean text to specify if the original column is one TEXT (CLOB) column (true). Defaults to false.
  1838. * @return string the piece of SQL code to be used in your statement.
  1839. */
  1840. function sql_cast_char2int($fieldname, $text=false) {
  1841. global $CFG;
  1842. $sql = '';
  1843. switch ($CFG->dbfamily) {
  1844. case 'mysql':
  1845. $sql = ' CAST(' . $fieldname . ' AS SIGNED) ';
  1846. break;
  1847. case 'postgres':
  1848. $sql = ' CAST(' . $fieldname . ' AS INT) ';
  1849. break;
  1850. case 'mssql':
  1851. if (!$text) {
  1852. $sql = ' CAST(' . $fieldname . ' AS INT) ';
  1853. } else {
  1854. $sql = ' CAST(' . sql_compare_text($fieldname) . ' AS INT) ';
  1855. }
  1856. break;
  1857. case 'oracle':
  1858. if (!$text) {
  1859. $sql = ' CAST(' . $fieldname . ' AS INT) ';
  1860. } else {
  1861. $sql = ' CAST(' . sql_compare_text($fieldname) . ' AS INT) ';
  1862. }
  1863. break;
  1864. default:
  1865. $sql = ' ' . $fieldname . ' ';
  1866. }
  1867. return $sql;
  1868. }
  1869. /**
  1870. * Returns the SQL text to be used in order to perform one bitwise AND operation
  1871. * between 2 integers.
  1872. * @param integer int1 first integer in the operation
  1873. * @param integer int2 second integer in the operation
  1874. * @return string the piece of SQL code to be used in your statement.
  1875. */
  1876. function sql_bitand($int1, $int2) {
  1877. global $CFG;
  1878. switch ($CFG->dbfamily) {
  1879. case 'oracle':
  1880. return 'bitand((' . $int1 . '), (' . $int2 . '))';
  1881. break;
  1882. default:
  1883. return '((' . $int1 . ') & (' . $int2 . '))';
  1884. }
  1885. }
  1886. /**
  1887. * Returns the SQL text to be used in order to perform one bitwise OR operation
  1888. * between 2 integers.
  1889. * @param integer int1 first integer in the operation
  1890. * @param integer int2 second integer in the operation
  1891. * @return string the piece of SQL code to be used in your statement.
  1892. */
  1893. function sql_bitor($int1, $int2) {
  1894. global $CFG;
  1895. switch ($CFG->dbfamily) {
  1896. case 'oracle':
  1897. return '((' . $int1 . ') + (' . $int2 . ') - ' . sql_bitand($int1, $int2) . ')';
  1898. break;
  1899. default:
  1900. return '((' . $int1 . ') | (' . $int2 . '))';
  1901. }
  1902. }
  1903. /**
  1904. * Returns the SQL text to be used in order to perform one bitwise XOR operation
  1905. * between 2 integers.
  1906. * @param integer int1 first integer in the operation
  1907. * @param integer int2 second integer in the operation
  1908. * @return string the piece of SQL code to be used in your statement.
  1909. */
  1910. function sql_bitxor($int1, $int2) {
  1911. global $CFG;
  1912. switch ($CFG->dbfamily) {
  1913. case 'oracle':
  1914. return '(' . sql_bitor($int1, $int2) . ' - ' . sql_bitand($int1, $int2) . ')';
  1915. break;
  1916. case 'postgres':
  1917. return '((' . $int1 . ') # (' . $int2 . '))';
  1918. break;
  1919. default:
  1920. return '((' . $int1 . ') ^ (' . $int2 . '))';
  1921. }
  1922. }
  1923. /**
  1924. * Returns the SQL text to be used in order to perform one bitwise NOT operation
  1925. * with 1 integer.
  1926. * @param integer int1 integer in the operation
  1927. * @return string the piece of SQL code to be used in your statement.
  1928. */
  1929. function sql_bitnot($int1) {
  1930. global $CFG;
  1931. switch ($CFG->dbfamily) {
  1932. case 'oracle':
  1933. return '((0 - (' . $int1 . ')) - 1)';
  1934. break;
  1935. default:
  1936. return '(~(' . $int1 . '))';
  1937. }
  1938. }
  1939. /**
  1940. * Returns the FROM clause required by some DBs in all SELECT statements
  1941. * To be used in queries not having FROM clause to provide cross_db
  1942. */
  1943. function sql_null_from_clause() {
  1944. global $CFG;
  1945. switch ($CFG->dbfamily) {
  1946. case 'oracle':
  1947. return ' FROM dual';
  1948. break;
  1949. default:
  1950. return '';
  1951. }
  1952. }
  1953. /**
  1954. * Returns the correct CEIL expression applied to fieldname
  1955. * @param string fieldname the field (or expression) we are going to ceil
  1956. * @return string the piece of SQL code to be used in your ceiling statement
  1957. */
  1958. function sql_ceil($fieldname) {
  1959. global $CFG;
  1960. switch ($CFG->dbfamily) {
  1961. case 'mssql':
  1962. return ' CEILING(' . $fieldname . ')';
  1963. break;
  1964. default:
  1965. return ' CEIL(' . $fieldname . ')';
  1966. }
  1967. }
  1968. /**
  1969. * Prepare a SQL WHERE clause to select records where the given fields match the given values.
  1970. *
  1971. * Prepares a where clause of the form
  1972. * WHERE field1 = value1 AND field2 = value2 AND field3 = value3
  1973. * except that you need only specify as many arguments (zero to three) as you need.
  1974. *
  1975. * @param string $field1 the first field to check (optional).
  1976. * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
  1977. * @param string $field2 the second field to check (optional).
  1978. * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
  1979. * @param string $field3 the third field to check (optional).
  1980. * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
  1981. */
  1982. function where_clause($field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
  1983. if ($field1) {
  1984. $select = is_null($value1) ? "WHERE $field1 IS NULL" : "WHERE $field1 = '$value1'";
  1985. if ($field2) {
  1986. $select .= is_null($value2) ? " AND $field2 IS NULL" : " AND $field2 = '$value2'";
  1987. if ($field3) {
  1988. $select .= is_null($value3) ? " AND $field3 IS NULL" : " AND $field3 = '$value3'";
  1989. }
  1990. }
  1991. } else {
  1992. $select = '';
  1993. }
  1994. return $select;
  1995. }
  1996. /**
  1997. * Get the data type of a table column, using an ADOdb MetaType() call.
  1998. *
  1999. * @uses $CFG
  2000. * @uses $db
  2001. * @param string $table The name of the database table
  2002. * @param string $column The name of the field in the table
  2003. * @return string Field type or false if error
  2004. */
  2005. function column_type($table, $column) {
  2006. global $CFG, $db;
  2007. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
  2008. $sql = 'SELECT '.$column.' FROM '.$CFG->prefix.$table.' WHERE 1=2';
  2009. if(!$rs = $db->Execute($sql)) {
  2010. debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
  2011. if (!empty($CFG->dblogerror)) {
  2012. $debug=array_shift(debug_backtrace());
  2013. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
  2014. }
  2015. return false;
  2016. }
  2017. $field = $rs->FetchField(0);
  2018. return $rs->MetaType($field->type);
  2019. }
  2020. /**
  2021. * This function will execute an array of SQL commands, returning
  2022. * true/false if any error is found and stopping/continue as desired.
  2023. * It's widely used by all the ddllib.php functions
  2024. *
  2025. * @param array sqlarr array of sql statements to execute
  2026. * @param boolean continue to specify if must continue on error (true) or stop (false)
  2027. * @param boolean feedback to specify to show status info (true) or not (false)
  2028. * @param boolean true if everything was ok, false if some error was found
  2029. */
  2030. function execute_sql_arr($sqlarr, $continue=true, $feedback=true) {
  2031. if (!is_array($sqlarr)) {
  2032. return false;
  2033. }
  2034. $status = true;
  2035. foreach($sqlarr as $sql) {
  2036. if (!execute_sql($sql, $feedback)) {
  2037. $status = false;
  2038. if (!$continue) {
  2039. break;
  2040. }
  2041. }
  2042. }
  2043. return $status;
  2044. }
  2045. /**
  2046. * This internal function, called from setup.php, sets all the configuration
  2047. * needed to work properly against any DB. It setups connection encoding
  2048. * and some other variables.
  2049. *
  2050. * This function must contain the init code needed for each dbtype supported.
  2051. */
  2052. function configure_dbconnection() {
  2053. global $CFG, $db;
  2054. switch ($CFG->dbtype) {
  2055. case 'mysql':
  2056. case 'mysqli':
  2057. $db->Execute("SET NAMES 'utf8'");
  2058. break;
  2059. case 'postgres7':
  2060. $db->Execute("SET NAMES 'utf8'");
  2061. break;
  2062. case 'db2':
  2063. break;
  2064. case 'mssql':
  2065. case 'mssql_n':
  2066. case 'odbc_mssql':
  2067. /// No need to set charset. It must be specified in the driver conf
  2068. /// Allow quoted identifiers
  2069. $db->Execute('SET QUOTED_IDENTIFIER ON');
  2070. /// Force ANSI nulls so the NULL check was done by IS NULL and NOT IS NULL
  2071. /// instead of equal(=) and distinct(<>) simbols
  2072. $db->Execute('SET ANSI_NULLS ON');
  2073. /// Enable sybase quotes, so addslashes and stripslashes will use "'"
  2074. ini_set('magic_quotes_sybase', '1');
  2075. /// NOTE: Not 100% useful because GPC has been addslashed with the setting off
  2076. /// so IT'S MANDATORY TO CHANGE THIS UNDER php.ini or .htaccess for this DB
  2077. /// or to turn off magic_quotes to allow Moodle to do it properly
  2078. break;
  2079. case 'oci8po':
  2080. /// No need to set charset. It must be specified by the NLS_LANG env. variable
  2081. /// Enable sybase quotes, so addslashes and stripslashes will use "'"
  2082. ini_set('magic_quotes_sybase', '1');
  2083. /// NOTE: Not 100% useful because GPC has been addslashed with the setting off
  2084. /// so IT'S MANDATORY TO ENABLE THIS UNDER php.ini or .htaccess for this DB
  2085. /// or to turn off magic_quotes to allow Moodle to do it properly
  2086. /// Now set the decimal separator to DOT, Moodle & PHP will always send floats to
  2087. /// DB using DOTS. Manually introduced floats (if using other characters) must be
  2088. /// converted back to DOTs (like gradebook does)
  2089. $db->Execute("ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,'");
  2090. break;
  2091. }
  2092. }
  2093. /**
  2094. * This function will handle all the records before being inserted/updated to DB for Oracle
  2095. * installations. This is because the "special feature" of Oracle where the empty string is
  2096. * equal to NULL and this presents a problem with all our currently NOT NULL default '' fields.
  2097. *
  2098. * Once Moodle DB will be free of this sort of false NOT NULLS, this hack could be removed safely
  2099. *
  2100. * Note that this function is 100% private and should be used, exclusively by DML functions
  2101. * in this file. Also, this is considered a DIRTY HACK to be removed when possible. (stronk7)
  2102. *
  2103. * This function is private and must not be used outside dmllib at all
  2104. *
  2105. * @param $table string the table where the record is going to be inserted/updated (without prefix)
  2106. * @param $dataobject object the object to be inserted/updated
  2107. * @param $usecache boolean flag to determinate if we must use the per request cache of metadata
  2108. * true to use it, false to ignore and delete it
  2109. */
  2110. function oracle_dirty_hack ($table, &$dataobject, $usecache = true) {
  2111. global $CFG, $db, $metadata_cache;
  2112. /// Init and delete metadata cache
  2113. if (!isset($metadata_cache) || !$usecache) {
  2114. $metadata_cache = array();
  2115. }
  2116. /// For Oracle DB, empty strings are converted to NULLs in DB
  2117. /// and this breaks a lot of NOT NULL columns currenty Moodle. In the future it's
  2118. /// planned to move some of them to NULL, if they must accept empty values and this
  2119. /// piece of code will become less and less used. But, for now, we need it.
  2120. /// What we are going to do is to examine all the data being inserted and if it's
  2121. /// an empty string (NULL for Oracle) and the field is defined as NOT NULL, we'll modify
  2122. /// such data in the best form possible ("0" for booleans and numbers and " " for the
  2123. /// rest of strings. It isn't optimal, but the only way to do so.
  2124. /// In the oppsite, when retrieving records from Oracle, we'll decode " " back to
  2125. /// empty strings to allow everything to work properly. DIRTY HACK.
  2126. /// If the db isn't Oracle, return without modif
  2127. if ( $CFG->dbfamily != 'oracle') {
  2128. return;
  2129. }
  2130. /// Get Meta info to know what to change, using the cached meta if exists
  2131. if (!isset($metadata_cache[$table])) {
  2132. $metadata_cache[$table] = array_change_key_case($db->MetaColumns($CFG->prefix . $table), CASE_LOWER);
  2133. }
  2134. $columns = $metadata_cache[$table];
  2135. /// Iterate over all the fields in the insert, transforming values
  2136. /// in the best possible form
  2137. foreach ($dataobject as $fieldname => $fieldvalue) {
  2138. /// If the field doesn't exist in metadata, skip
  2139. if (!isset($columns[strtolower($fieldname)])) {
  2140. continue;
  2141. }
  2142. /// If the field ins't VARCHAR or CLOB, skip
  2143. if ($columns[strtolower($fieldname)]->type != 'VARCHAR2' && $columns[strtolower($fieldname)]->type != 'CLOB') {
  2144. continue;
  2145. }
  2146. /// If the field isn't NOT NULL, skip (it's nullable, so accept empty values)
  2147. if (!$columns[strtolower($fieldname)]->not_null) {
  2148. continue;
  2149. }
  2150. /// If the value isn't empty, skip
  2151. if (!empty($fieldvalue)) {
  2152. continue;
  2153. }
  2154. /// Now, we have one empty value, going to be inserted to one NOT NULL, VARCHAR2 or CLOB field
  2155. /// Try to get the best value to be inserted
  2156. /// The '0' string doesn't need any transformation, skip
  2157. if ($fieldvalue === '0') {
  2158. continue;
  2159. }
  2160. /// Transformations start
  2161. if (gettype($fieldvalue) == 'boolean') {
  2162. $dataobject->$fieldname = '0'; /// Transform false to '0' that evaluates the same for PHP
  2163. } else if (gettype($fieldvalue) == 'integer') {
  2164. $dataobject->$fieldname = '0'; /// Transform 0 to '0' that evaluates the same for PHP
  2165. } else if (gettype($fieldvalue) == 'NULL') {
  2166. $dataobject->$fieldname = '0'; /// Transform NULL to '0' that evaluates the same for PHP
  2167. } else if ($fieldvalue === '') {
  2168. $dataobject->$fieldname = ' '; /// Transform '' to ' ' that DONT'T EVALUATE THE SAME
  2169. /// (we'll transform back again on get_records_XXX functions and others)!!
  2170. }
  2171. }
  2172. }
  2173. /// End of DIRTY HACK
  2174. /**
  2175. * This function will search for all the CLOBs and BLOBs fields passed in the dataobject, replacing
  2176. * their contents by the fixed strings '@#CLOB#@' and '@#BLOB#@' and returning one array for all the
  2177. * found CLOBS and another for all the found BLOBS
  2178. * Used by Oracle drivers to perform the two-step insertion/update of LOBs and
  2179. * by MSSQL to perform the same exclusively for BLOBs (IMAGE fields)
  2180. *
  2181. * This function is private and must not be used outside dmllib at all
  2182. *
  2183. * @param $table string the table where the record is going to be inserted/updated (without prefix)
  2184. * @param $dataobject object the object to be inserted/updated
  2185. * @param $clobs array of clobs detected
  2186. * @param $dataobject array of blobs detected
  2187. * @param $unset boolean to specify if we must unset found LOBs from the original object (true) or
  2188. * just return them modified to @#CLOB#@ and @#BLOB#@ (false)
  2189. * @param $usecache boolean flag to determinate if we must use the per request cache of metadata
  2190. * true to use it, false to ignore and delete it
  2191. */
  2192. function db_detect_lobs ($table, &$dataobject, &$clobs, &$blobs, $unset = false, $usecache = true) {
  2193. global $CFG, $db, $metadata_cache;
  2194. $dataarray = (array)$dataobject; //Convert to array. It's supposed that PHP 4.3 doesn't iterate over objects
  2195. /// Initial configuration, based on DB
  2196. switch ($CFG->dbfamily) {
  2197. case 'oracle':
  2198. $clobdbtype = 'CLOB'; //Name of clobs for this DB
  2199. $blobdbtype = 'BLOB'; //Name of blobs for this DB
  2200. break;
  2201. case 'mssql':
  2202. $clobdbtype = 'NOTPROCESSES'; //Name of clobs for this DB (under mssql flavours we don't process CLOBS)
  2203. $blobdbtype = 'IMAGE'; //Name of blobs for this DB
  2204. break;
  2205. case 'postgres':
  2206. $clobdbtype = 'NOTPROCESSES'; //Name of clobs for this DB (under postgres flavours we don't process CLOBS)
  2207. $blobdbtype = 'BYTEA'; //Name of blobs for this DB
  2208. break;
  2209. default:
  2210. return; //Other DB doesn't need this two step to happen, prevent continue
  2211. }
  2212. /// Init and delete metadata cache
  2213. if (!isset($metadata_cache) || !$usecache) {
  2214. $metadata_cache = array();
  2215. }
  2216. /// Get Meta info to know what to change, using the cached meta if exists
  2217. if (!isset($metadata_cache[$table])) {
  2218. $metadata_cache[$table] = array_change_key_case($db->MetaColumns($CFG->prefix . $table), CASE_LOWER);
  2219. }
  2220. $columns = $metadata_cache[$table];
  2221. foreach ($dataarray as $fieldname => $fieldvalue) {
  2222. /// If the field doesn't exist in metadata, skip
  2223. if (!isset($columns[strtolower($fieldname)])) {
  2224. continue;
  2225. }
  2226. /// If the field is CLOB, update its value to '@#CLOB#@' and store it in the $clobs array
  2227. if (strtoupper($columns[strtolower($fieldname)]->type) == $clobdbtype) {
  2228. /// Oracle optimization. CLOBs under 4000cc can be directly inserted (no need to apply 2-phases to them)
  2229. if ($CFG->dbfamily == 'oracle' && strlen($dataobject->$fieldname) < 4000) {
  2230. continue;
  2231. }
  2232. $clobs[$fieldname] = $dataobject->$fieldname;
  2233. if ($unset) {
  2234. unset($dataobject->$fieldname);
  2235. } else {
  2236. $dataobject->$fieldname = '@#CLOB#@';
  2237. }
  2238. continue;
  2239. }
  2240. /// If the field is BLOB OR IMAGE OR BYTEA, update its value to '@#BLOB#@' and store it in the $blobs array
  2241. if (strtoupper($columns[strtolower($fieldname)]->type) == $blobdbtype) {
  2242. $blobs[$fieldname] = $dataobject->$fieldname;
  2243. if ($unset) {
  2244. unset($dataobject->$fieldname);
  2245. } else {
  2246. $dataobject->$fieldname = '@#BLOB#@';
  2247. }
  2248. continue;
  2249. }
  2250. }
  2251. }
  2252. /**
  2253. * This function will iterate over $clobs and $blobs array, executing the needed
  2254. * UpdateClob() and UpdateBlob() ADOdb function calls to store LOBs contents properly
  2255. * Records to be updated are always searched by PK (id always!)
  2256. *
  2257. * Used by Orace CLOBS and BLOBS and MSSQL IMAGES
  2258. *
  2259. * This function is private and must not be used outside dmllib at all
  2260. *
  2261. * @param $table string the table where the record is going to be inserted/updated (without prefix)
  2262. * @param $sqlcondition mixed value defining the records to be LOB-updated. It it's a number, must point
  2263. * to the PK og the table (id field), else it's processed as one harcoded SQL condition (WHERE clause)
  2264. * @param $clobs array of clobs to be updated
  2265. * @param $blobs array of blobs to be updated
  2266. */
  2267. function db_update_lobs ($table, $sqlcondition, &$clobs, &$blobs) {
  2268. global $CFG, $db;
  2269. $status = true;
  2270. /// Initial configuration, based on DB
  2271. switch ($CFG->dbfamily) {
  2272. case 'oracle':
  2273. $clobdbtype = 'CLOB'; //Name of clobs for this DB
  2274. $blobdbtype = 'BLOB'; //Name of blobs for this DB
  2275. break;
  2276. case 'mssql':
  2277. $clobdbtype = 'NOTPROCESSES'; //Name of clobs for this DB (under mssql flavours we don't process CLOBS)
  2278. $blobdbtype = 'IMAGE'; //Name of blobs for this DB
  2279. break;
  2280. case 'postgres':
  2281. $clobdbtype = 'NOTPROCESSES'; //Name of clobs for this DB (under postgres flavours we don't process CLOBS)
  2282. $blobdbtype = 'BYTEA'; //Name of blobs for this DB
  2283. break;
  2284. default:
  2285. return; //Other DB doesn't need this two step to happen, prevent continue
  2286. }
  2287. /// Calculate the update sql condition
  2288. if (is_numeric($sqlcondition)) { /// If passing a number, it's the PK of the table (id)
  2289. $sqlcondition = 'id=' . $sqlcondition;
  2290. } else { /// Else, it's a formal standard SQL condition, we try to delete the WHERE in case it exists
  2291. $sqlcondition = trim(preg_replace('/^WHERE/is', '', trim($sqlcondition)));
  2292. }
  2293. /// Update all the clobs
  2294. if ($clobs) {
  2295. foreach ($clobs as $key => $value) {
  2296. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; }; /// Count the extra updates in PERF
  2297. /// Oracle CLOBs doesn't like quoted strings (are inserted via prepared statemets)
  2298. if ($CFG->dbfamily == 'oracle') {
  2299. $value = stripslashes_safe($value);
  2300. }
  2301. if (!$db->UpdateClob($CFG->prefix.$table, $key, $value, $sqlcondition)) {
  2302. $status = false;
  2303. $statement = "UpdateClob('$CFG->prefix$table', '$key', '" . substr($value, 0, 100) . "...', '$sqlcondition')";
  2304. debugging($db->ErrorMsg() ."<br /><br />".s($statement));
  2305. if (!empty($CFG->dblogerror)) {
  2306. $debug=array_shift(debug_backtrace());
  2307. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $statement");
  2308. }
  2309. }
  2310. }
  2311. }
  2312. /// Update all the blobs
  2313. if ($blobs) {
  2314. foreach ($blobs as $key => $value) {
  2315. if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; }; /// Count the extra updates in PERF
  2316. /// Oracle, MSSQL and PostgreSQL BLOBs doesn't like quoted strings (are inserted via prepared statemets)
  2317. if ($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') {
  2318. $value = stripslashes_safe($value);
  2319. }
  2320. if(!$db->UpdateBlob($CFG->prefix.$table, $key, $value, $sqlcondition)) {
  2321. $status = false;
  2322. $statement = "UpdateBlob('$CFG->prefix$table', '$key', '" . substr($value, 0, 100) . "...', '$sqlcondition')";
  2323. debugging($db->ErrorMsg() ."<br /><br />".s($statement));
  2324. if (!empty($CFG->dblogerror)) {
  2325. $debug=array_shift(debug_backtrace());
  2326. error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $statement");
  2327. }
  2328. }
  2329. }
  2330. }
  2331. return $status;
  2332. }
  2333. /**
  2334. * Set cached record.
  2335. *
  2336. * If you have called rcache_getforfill() before, it will also
  2337. * release the lock.
  2338. *
  2339. * This function is private and must not be used outside dmllib at all
  2340. *
  2341. * @param $table string
  2342. * @param $id integer
  2343. * @param $rec obj
  2344. * @return bool
  2345. */
  2346. function rcache_set($table, $id, $rec) {
  2347. global $CFG, $MCACHE, $rcache;
  2348. if ($CFG->cachetype === 'internal') {
  2349. if (!isset($rcache->data[$table])) {
  2350. $rcache->data[$table] = array();
  2351. }
  2352. if (!isset($rcache->data[$table][$id]) and count($rcache->data[$table]) > $CFG->intcachemax) {
  2353. // release oldes record
  2354. reset($rcache->data[$table]);
  2355. $key = key($rcache->data[$table]);
  2356. unset($rcache->data[$table][$key]);
  2357. }
  2358. $rcache->data[$table][$id] = clone($rec);
  2359. } else {
  2360. $key = $table . '|' . $id;
  2361. if (isset($MCACHE)) {
  2362. // $table is a flag used to mark
  2363. // a table as dirty & uncacheable
  2364. // when an UPDATE or DELETE not bound by ID
  2365. // is taking place
  2366. if (!$MCACHE->get($table)) {
  2367. // this will also release the _forfill lock
  2368. $MCACHE->set($key, $rec, $CFG->rcachettl);
  2369. }
  2370. }
  2371. }
  2372. return true;
  2373. }
  2374. /**
  2375. * Unset cached record if it exists.
  2376. *
  2377. * This function is private and must not be used outside dmllib at all
  2378. *
  2379. * @param $table string
  2380. * @param $id integer
  2381. * @return bool
  2382. */
  2383. function rcache_unset($table, $id) {
  2384. global $CFG, $MCACHE, $rcache;
  2385. if ($CFG->cachetype === 'internal') {
  2386. if (isset($rcache->data[$table][$id])) {
  2387. unset($rcache->data[$table][$id]);
  2388. }
  2389. } else {
  2390. $key = $table . '|' . $id;
  2391. if (isset($MCACHE)) {
  2392. $MCACHE->delete($key);
  2393. }
  2394. }
  2395. return true;
  2396. }
  2397. /**
  2398. * Get cached record if available. ONLY use if you
  2399. * are trying to get the cached record and will NOT
  2400. * fetch it yourself if not cached.
  2401. *
  2402. * Use rcache_getforfill() if you are going to fetch
  2403. * the record if not cached...
  2404. *
  2405. * This function is private and must not be used outside dmllib at all
  2406. *
  2407. * @param $table string
  2408. * @param $id integer
  2409. * @return mixed object-like record on cache hit, false otherwise
  2410. */
  2411. function rcache_get($table, $id) {
  2412. global $CFG, $MCACHE, $rcache;
  2413. if ($CFG->cachetype === 'internal') {
  2414. if (isset($rcache->data[$table][$id])) {
  2415. $rcache->hits++;
  2416. return clone($rcache->data[$table][$id]);
  2417. } else {
  2418. $rcache->misses++;
  2419. return false;
  2420. }
  2421. }
  2422. if (isset($MCACHE)) {
  2423. $key = $table . '|' . $id;
  2424. // we set $table as a flag used to mark
  2425. // a table as dirty & uncacheable
  2426. // when an UPDATE or DELETE not bound by ID
  2427. // is taking place
  2428. if ($MCACHE->get($table)) {
  2429. $rcache->misses++;
  2430. return false;
  2431. } else {
  2432. $rec = $MCACHE->get($key);
  2433. if (!empty($rec)) {
  2434. $rcache->hits++;
  2435. return $rec;
  2436. } else {
  2437. $rcache->misses++;
  2438. return false;
  2439. }
  2440. }
  2441. }
  2442. return false;
  2443. }
  2444. /**
  2445. * Get cached record if available. In most cases you want
  2446. * to use this function -- namely if you are trying to get
  2447. * the cached record and will fetch it yourself if not cached.
  2448. * (and set the cache ;-)
  2449. *
  2450. * Uses the getforfill caching mechanism. See lib/eaccelerator.class.php
  2451. * for a detailed description of the technique.
  2452. *
  2453. * Note: if you call rcache_getforfill() you are making an implicit promise
  2454. * that if the cache is empty, you will later populate it, or cancel the promise
  2455. * calling rcache_releaseforfill();
  2456. *
  2457. * This function is private and must not be used outside dmllib at all
  2458. *
  2459. * @param $table string
  2460. * @param $id integer
  2461. * @return mixed object-like record on cache hit, false otherwise
  2462. */
  2463. function rcache_getforfill($table, $id) {
  2464. global $CFG, $MCACHE, $rcache;
  2465. if ($CFG->cachetype === 'internal') {
  2466. return rcache_get($table, $id);
  2467. }
  2468. if (isset($MCACHE)) {
  2469. $key = $table . '|' . $id;
  2470. // if $table is set - we won't take the
  2471. // lock either
  2472. if ($MCACHE->get($table)) {
  2473. $rcache->misses++;
  2474. return false;
  2475. }
  2476. $rec = $MCACHE->getforfill($key);
  2477. if (!empty($rec)) {
  2478. $rcache->hits++;
  2479. return $rec;
  2480. }
  2481. $rcache->misses++;
  2482. return false;
  2483. }
  2484. return false;
  2485. }
  2486. /**
  2487. * Release the exclusive lock obtained by
  2488. * rcache_getforfill(). See rcache_getforfill()
  2489. * for more details.
  2490. *
  2491. * This function is private and must not be used outside dmllib at all
  2492. *
  2493. * @param $table string
  2494. * @param $id integer
  2495. * @return bool
  2496. */
  2497. function rcache_releaseforfill($table, $id) {
  2498. global $CFG, $MCACHE;
  2499. if (isset($MCACHE)) {
  2500. $key = $table . '|' . $id;
  2501. return $MCACHE->releaseforfill($key);
  2502. }
  2503. return true;
  2504. }
  2505. /**
  2506. * Remove or invalidate all rcache entries related to
  2507. * a table. Not all caching mechanisms cluster entries
  2508. * by table so in those cases we use alternative strategies.
  2509. *
  2510. * This function is private and must not be used outside dmllib at all
  2511. *
  2512. * @param $table string the table to invalidate records for
  2513. * @return bool
  2514. */
  2515. function rcache_unset_table ($table) {
  2516. global $CFG, $MCACHE, $rcache;
  2517. if ($CFG->cachetype === 'internal') {
  2518. if (isset($rcache->data[$table])) {
  2519. unset($rcache->data[$table]);
  2520. }
  2521. return true;
  2522. }
  2523. if (isset($MCACHE)) {
  2524. // at least as long as content keys to ensure they expire
  2525. // before the dirty flag
  2526. $MCACHE->set($table, true, $CFG->rcachettl);
  2527. }
  2528. return true;
  2529. }
  2530. ?>