MDL-20827 workshop: set correct wtype on newly created instances
[moodle.git] / lib / dmllib.php
blobb049b23480a01373e1a77c137718229d4df9b16c
1 <?php // $Id$
3 ///////////////////////////////////////////////////////////////////////////
4 // //
5 // NOTICE OF COPYRIGHT //
6 // //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment //
8 // http://moodle.com //
9 // //
10 // Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
11 // //
12 // This program is free software; you can redistribute it and/or modify //
13 // it under the terms of the GNU General Public License as published by //
14 // the Free Software Foundation; either version 2 of the License, or //
15 // (at your option) any later version. //
16 // //
17 // This program is distributed in the hope that it will be useful, //
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
20 // GNU General Public License for more details: //
21 // //
22 // http://www.gnu.org/copyleft/gpl.html //
23 // //
24 ///////////////////////////////////////////////////////////////////////////
26 /// This library contains all the Data Manipulation Language (DML) functions
27 /// used to interact with the DB. All the dunctions in this library must be
28 /// generic and work against the major number of RDBMS possible. This is the
29 /// list of currently supported and tested DBs: mysql, postresql, mssql, oracle
31 /// This library is automatically included by Moodle core so you never need to
32 /// include it yourself.
34 /// For more info about the functions available in this library, please visit:
35 /// http://docs.moodle.org/en/DML_functions
36 /// (feel free to modify, improve and document such page, thanks!)
38 /// GLOBAL CONSTANTS /////////////////////////////////////////////////////////
40 $empty_rs_cache = array(); // Keeps copies of the recordsets used in one invocation
41 $metadata_cache = array(); // Kereeps copies of the MetaColumns() for each table used in one invocations
43 $rcache = new StdClass; // Cache simple get_record results
44 $rcache->data = array();
45 $rcache->hits = 0;
46 $rcache->misses = 0;
48 /// FUNCTIONS FOR DATABASE HANDLING ////////////////////////////////
50 /**
51 * Execute a given sql command string
53 * Completely general function - it just runs some SQL and reports success.
55 * @uses $db
56 * @param string $command The sql string you wish to be executed.
57 * @param bool $feedback Set this argument to true if the results generated should be printed. Default is true.
58 * @return bool success
60 function execute_sql($command, $feedback=true) {
61 /// Completely general function - it just runs some SQL and reports success.
63 global $db, $CFG;
65 $olddebug = $db->debug;
67 if (!$feedback) {
68 $db->debug = false;
71 if ($CFG->version >= 2006101007) { //Look for trailing ; from Moodle 1.7.0
72 $command = trim($command);
73 /// If the trailing ; is there, fix and warn!
74 if (substr($command, strlen($command)-1, 1) == ';') {
75 /// One noticeable exception, Oracle PL/SQL blocks require ending in ";"
76 if ($CFG->dbfamily == 'oracle' && substr($command, -4) == 'END;') {
77 /// Nothing to fix/warn. The command is one PL/SQL block, so it's ok.
78 } else {
79 $command = trim($command, ';');
80 debugging('Warning. Avoid to end your SQL commands with a trailing ";".', DEBUG_DEVELOPER);
85 $empty_rs_cache = array(); // Clear out the cache, just in case changes were made to table structures
87 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
89 $rs = $db->Execute($command);
91 $db->debug = $olddebug;
93 if ($rs) {
94 if ($feedback) {
95 notify(get_string('success'), 'notifysuccess');
97 return true;
98 } else {
99 if ($feedback) {
100 notify('<strong>' . get_string('error') . '</strong>');
102 // these two may go to difference places
103 debugging($db->ErrorMsg() .'<br /><br />'. s($command));
104 if (!empty($CFG->dblogerror)) {
105 $debug=array_shift(debug_backtrace());
106 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $command");
108 return false;
113 * on DBs that support it, switch to transaction mode and begin a transaction
114 * you'll need to ensure you call commit_sql() or your changes *will* be lost.
116 * Now using ADOdb standard transactions. Some day, we should switch to
117 * Smart Transactions (http://phplens.com/adodb/tutorial.smart.transactions.html)
118 * as they autodetect errors and are nestable and easier to write
120 * this is _very_ useful for massive updates
122 function begin_sql() {
124 global $db;
126 $db->BeginTrans();
128 return true;
132 * on DBs that support it, commit the transaction
134 * Now using ADOdb standard transactions. Some day, we should switch to
135 * Smart Transactions (http://phplens.com/adodb/tutorial.smart.transactions.html)
136 * as they autodetect errors and are nestable and easier to write
138 function commit_sql() {
140 global $db;
142 $db->CommitTrans();
144 return true;
148 * on DBs that support it, rollback the transaction
150 * Now using ADOdb standard transactions. Some day, we should switch to
151 * Smart Transactions (http://phplens.com/adodb/tutorial.smart.transactions.html)
152 * as they autodetect errors and are nestable and easier to write
154 function rollback_sql() {
156 global $db;
158 $db->RollbackTrans();
160 return true;
164 * returns db specific uppercase function
165 * @deprecated Moodle 1.7 because all the RDBMS use upper()
167 function db_uppercase() {
168 return "upper";
172 * returns db specific lowercase function
173 * @deprecated Moodle 1.7 because all the RDBMS use lower()
175 function db_lowercase() {
176 return "lower";
181 * Run an arbitrary sequence of semicolon-delimited SQL commands
183 * Assumes that the input text (file or string) consists of
184 * a number of SQL statements ENDING WITH SEMICOLONS. The
185 * semicolons MUST be the last character in a line.
186 * Lines that are blank or that start with "#" or "--" (postgres) are ignored.
187 * Only tested with mysql dump files (mysqldump -p -d moodle)
189 * @uses $CFG
191 * @deprecated Moodle 1.7 use the new XMLDB stuff in lib/ddllib.php
193 * @param string $sqlfile The path where a file with sql commands can be found on the server.
194 * @param string $sqlstring If no path is supplied then a string with semicolon delimited sql
195 * commands can be supplied in this argument.
196 * @return bool Returns true if databse was modified successfully.
198 function modify_database($sqlfile='', $sqlstring='') {
200 global $CFG;
202 if ($CFG->version > 2006101007) {
203 debugging('Function modify_database() is deprecated. Replace it with the new XMLDB stuff.', DEBUG_DEVELOPER);
206 $success = true; // Let's be optimistic
208 if (!empty($sqlfile)) {
209 if (!is_readable($sqlfile)) {
210 $success = false;
211 echo '<p>Tried to modify database, but "'. $sqlfile .'" doesn\'t exist!</p>';
212 return $success;
213 } else {
214 $lines = file($sqlfile);
216 } else {
217 $sqlstring = trim($sqlstring);
218 if ($sqlstring{strlen($sqlstring)-1} != ";") {
219 $sqlstring .= ";"; // add it in if it's not there.
221 $lines[] = $sqlstring;
224 $command = '';
226 foreach ($lines as $line) {
227 $line = rtrim($line);
228 $length = strlen($line);
230 if ($length and $line[0] <> '#' and $line[0].$line[1] <> '--') {
231 if (substr($line, $length-1, 1) == ';') {
232 $line = substr($line, 0, $length-1); // strip ;
233 $command .= $line;
234 $command = str_replace('prefix_', $CFG->prefix, $command); // Table prefixes
235 if (! execute_sql($command)) {
236 $success = false;
238 $command = '';
239 } else {
240 $command .= $line;
245 return $success;
249 /// GENERIC FUNCTIONS TO CHECK AND COUNT RECORDS ////////////////////////////////////////
252 * Test whether a record exists in a table where all the given fields match the given values.
254 * The record to test is specified by giving up to three fields that must
255 * equal the corresponding values.
257 * @uses $CFG
258 * @param string $table The table to check.
259 * @param string $field1 the first field to check (optional).
260 * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
261 * @param string $field2 the second field to check (optional).
262 * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
263 * @param string $field3 the third field to check (optional).
264 * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
265 * @return bool true if a matching record exists, else false.
267 function record_exists($table, $field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
269 global $CFG;
271 $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
273 return record_exists_sql('SELECT * FROM '. $CFG->prefix . $table .' '. $select);
277 * Test whether any records exists in a table which match a particular WHERE clause.
279 * @uses $CFG
280 * @param string $table The database table to be checked against.
281 * @param string $select A fragment of SQL to be used in a WHERE clause in the SQL call.
282 * @return bool true if a matching record exists, else false.
284 function record_exists_select($table, $select='') {
286 global $CFG;
288 if ($select) {
289 $select = 'WHERE '.$select;
292 return record_exists_sql('SELECT * FROM '. $CFG->prefix . $table . ' ' . $select);
296 * Test whether a SQL SELECT statement returns any records.
298 * This function returns true if the SQL statement executes
299 * without any errors and returns at least one record.
301 * @param string $sql The SQL statement to execute.
302 * @return bool true if the SQL executes without errors and returns at least one record.
304 function record_exists_sql($sql) {
306 $limitfrom = 0; /// Number of records to skip
307 $limitnum = 1; /// Number of records to retrieve
309 if (!$rs = get_recordset_sql($sql, $limitfrom, $limitnum)) {
310 return false;
313 if (rs_EOF($rs)) {
314 $result = false;
315 } else {
316 $result = true;
319 rs_close($rs);
320 return $result;
324 * Count the records in a table where all the given fields match the given values.
326 * @uses $CFG
327 * @param string $table The table to query.
328 * @param string $field1 the first field to check (optional).
329 * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
330 * @param string $field2 the second field to check (optional).
331 * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
332 * @param string $field3 the third field to check (optional).
333 * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
334 * @return int The count of records returned from the specified criteria.
336 function count_records($table, $field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
338 global $CFG;
340 $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
342 return count_records_sql('SELECT COUNT(*) FROM '. $CFG->prefix . $table .' '. $select);
346 * Count the records in a table which match a particular WHERE clause.
348 * @uses $CFG
349 * @param string $table The database table to be checked against.
350 * @param string $select A fragment of SQL to be used in a WHERE clause in the SQL call.
351 * @param string $countitem The count string to be used in the SQL call. Default is COUNT(*).
352 * @return int The count of records returned from the specified criteria.
354 function count_records_select($table, $select='', $countitem='COUNT(*)') {
356 global $CFG;
358 if ($select) {
359 $select = 'WHERE '.$select;
362 return count_records_sql('SELECT '. $countitem .' FROM '. $CFG->prefix . $table .' '. $select);
366 * Get the result of a SQL SELECT COUNT(...) query.
368 * Given a query that counts rows, return that count. (In fact,
369 * given any query, return the first field of the first record
370 * returned. However, this method should only be used for the
371 * intended purpose.) If an error occurrs, 0 is returned.
373 * @uses $CFG
374 * @uses $db
375 * @param string $sql The SQL string you wish to be executed.
376 * @return int the count. If an error occurrs, 0 is returned.
378 function count_records_sql($sql) {
379 $rs = get_recordset_sql($sql);
381 if (is_object($rs) and is_array($rs->fields)) {
382 return reset($rs->fields);
383 } else {
384 return 0;
388 /// GENERIC FUNCTIONS TO GET, INSERT, OR UPDATE DATA ///////////////////////////////////
392 * Get a single record as an object
394 * @uses $CFG
395 * @param string $table The table to select from.
396 * @param string $field1 the first field to check (optional).
397 * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
398 * @param string $field2 the second field to check (optional).
399 * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
400 * @param string $field3 the third field to check (optional).
401 * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
402 * @return mixed a fieldset object containing the first mathcing record, or false if none found.
404 function get_record($table, $field1, $value1, $field2='', $value2='', $field3='', $value3='', $fields='*') {
406 global $CFG;
408 // Check to see whether this record is eligible for caching (fields=*, only condition is id)
409 $docache = false;
410 if (!empty($CFG->rcache) && $CFG->rcache === true && $field1=='id' && !$field2 && !$field3 && $fields=='*') {
411 $docache = true;
412 // If it's in the cache, return it
413 $cached = rcache_getforfill($table, $value1);
414 if (!empty($cached)) {
415 return $cached;
419 $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
421 $record = get_record_sql('SELECT '.$fields.' FROM '. $CFG->prefix . $table .' '. $select);
423 // If we're caching records, store this one
424 // (supposing we got something - we don't cache failures)
425 if ($docache) {
426 if ($record !== false) {
427 rcache_set($table, $value1, $record);
428 } else {
429 rcache_releaseforfill($table, $value1);
432 return $record;
436 * Get a single record as an object using an SQL statement
438 * The SQL statement should normally only return one record. In debug mode
439 * you will get a warning if more record is returned (unless you
440 * set $expectmultiple to true). In non-debug mode, it just returns
441 * the first record.
443 * @uses $CFG
444 * @uses $db
445 * @param string $sql The SQL string you wish to be executed, should normally only return one record.
446 * @param bool $expectmultiple If the SQL cannot be written to conveniently return just one record,
447 * set this to true to hide the debug message.
448 * @param bool $nolimit sometimes appending ' LIMIT 1' to the SQL causes an error. Set this to true
449 * to stop your SQL being modified. This argument should probably be deprecated.
450 * @return Found record as object. False if not found or error
452 function get_record_sql($sql, $expectmultiple=false, $nolimit=false) {
454 global $CFG;
456 /// Default situation
457 $limitfrom = 0; /// Number of records to skip
458 $limitnum = 1; /// Number of records to retrieve
460 /// Only a few uses of the 2nd and 3rd parameter have been found
461 /// I think that we should avoid to use them completely, one
462 /// record is one record, and everything else should return error.
463 /// So the proposal is to change all the uses, (4-5 inside Moodle
464 /// Core), drop them from the definition and delete the next two
465 /// "if" sentences. (eloy, 2006-08-19)
467 if ($nolimit) {
468 $limitfrom = 0;
469 $limitnum = 0;
470 } else if ($expectmultiple) {
471 $limitfrom = 0;
472 $limitnum = 1;
473 } else if (debugging('', DEBUG_DEVELOPER)) {
474 // Debugging mode - don't use a limit of 1, but do change the SQL, because sometimes that
475 // causes errors, and in non-debug mode you don't see the error message and it is
476 // impossible to know what's wrong.
477 $limitfrom = 0;
478 $limitnum = 100;
481 if (!$rs = get_recordset_sql($sql, $limitfrom, $limitnum)) {
482 return false;
485 $recordcount = $rs->RecordCount();
487 if ($recordcount == 0) { // Found no records
488 return false;
490 } else if ($recordcount == 1) { // Found one record
491 /// DIRTY HACK to retrieve all the ' ' (1 space) fields converted back
492 /// to '' (empty string) for Oracle. It's the only way to work with
493 /// all those NOT NULL DEFAULT '' fields until we definitively delete them
494 if ($CFG->dbfamily == 'oracle') {
495 array_walk($rs->fields, 'onespace2empty');
497 /// End of DIRTY HACK
498 return (object)$rs->fields;
500 } else { // Error: found more than one record
501 notify('Error: Turn off debugging to hide this error.');
502 notify($sql . '(with limits ' . $limitfrom . ', ' . $limitnum . ')');
503 if ($records = $rs->GetAssoc(true)) {
504 notify('Found more than one record in get_record_sql !');
505 print_object($records);
506 } else {
507 notify('Very strange error in get_record_sql !');
508 print_object($rs);
510 print_continue("$CFG->wwwroot/$CFG->admin/config.php");
515 * Gets one record from a table, as an object
517 * @uses $CFG
518 * @param string $table The database table to be checked against.
519 * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
520 * @param string $fields A comma separated list of fields to be returned from the chosen table.
521 * @return object|false Returns an array of found records (as objects) or false if no records or error occured.
523 function get_record_select($table, $select='', $fields='*') {
525 global $CFG;
527 if ($select) {
528 $select = 'WHERE '. $select;
531 return get_record_sql('SELECT '. $fields .' FROM '. $CFG->prefix . $table .' '. $select);
535 * Get a number of records as an ADODB RecordSet.
537 * Selects records from the table $table.
539 * If specified, only records where the field $field has value $value are retured.
541 * If specified, the results will be sorted as specified by $sort. This
542 * is added to the SQL as "ORDER BY $sort". Example values of $sort
543 * mightbe "time ASC" or "time DESC".
545 * If $fields is specified, only those fields are returned.
547 * Since this method is a little less readable, use of it should be restricted to
548 * code where it's possible there might be large datasets being returned. For known
549 * small datasets use get_records - it leads to simpler code.
551 * If you only want some of the records, specify $limitfrom and $limitnum.
552 * The query will skip the first $limitfrom records (according to the sort
553 * order) and then return the next $limitnum records. If either of $limitfrom
554 * or $limitnum is specified, both must be present.
556 * The return value is an ADODB RecordSet object
557 * @link http://phplens.com/adodb/reference.functions.adorecordset.html
558 * if the query succeeds. If an error occurrs, false is returned.
560 * @param string $table the table to query.
561 * @param string $field a field to check (optional).
562 * @param string $value the value the field must have (requred if field1 is given, else optional).
563 * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
564 * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
565 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
566 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
567 * @return mixed an ADODB RecordSet object, or false if an error occured.
569 function get_recordset($table, $field='', $value='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
571 if ($field) {
572 $select = "$field = '$value'";
573 } else {
574 $select = '';
577 return get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
581 * Get a number of records as an ADODB RecordSet.
583 * If given, $select is used as the SELECT parameter in the SQL query,
584 * otherwise all records from the table are returned.
586 * Other arguments and the return type as for @see function get_recordset.
588 * @uses $CFG
589 * @param string $table the table to query.
590 * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
591 * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
592 * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
593 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
594 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
595 * @return mixed an ADODB RecordSet object, or false if an error occured.
597 function get_recordset_select($table, $select='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
599 global $CFG;
601 if ($select) {
602 $select = ' WHERE '. $select;
605 if ($sort) {
606 $sort = ' ORDER BY '. $sort;
609 return get_recordset_sql('SELECT '. $fields .' FROM '. $CFG->prefix . $table . $select . $sort, $limitfrom, $limitnum);
613 * Get a number of records as an ADODB RecordSet.
615 * Only records where $field takes one of the values $values are returned.
616 * $values should be a comma-separated list of values, for example "4,5,6,10"
617 * or "'foo','bar','baz'".
619 * Other arguments and the return type as for @see function get_recordset.
621 * @param string $table the table to query.
622 * @param string $field a field to check (optional).
623 * @param string $values comma separated list of values the field must have (requred if field is given, else optional).
624 * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
625 * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
626 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
627 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
628 * @return mixed an ADODB RecordSet object, or false if an error occured.
630 function get_recordset_list($table, $field='', $values='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
632 if ($field) {
633 $select = "$field IN ($values)";
634 } else {
635 $select = '';
638 return get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
642 * Get a number of records as an ADODB RecordSet. $sql must be a complete SQL query.
643 * Since this method is a little less readable, use of it should be restricted to
644 * code where it's possible there might be large datasets being returned. For known
645 * small datasets use get_records_sql - it leads to simpler code.
647 * The return type is as for @see function get_recordset.
649 * @uses $CFG
650 * @uses $db
651 * @param string $sql the SQL select query to execute.
652 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
653 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
654 * @return mixed an ADODB RecordSet object, or false if an error occured.
656 function get_recordset_sql($sql, $limitfrom=null, $limitnum=null) {
657 global $CFG, $db;
659 if (empty($db)) {
660 return false;
663 /// Temporary hack as part of phasing out all access to obsolete user tables XXX
664 if (!empty($CFG->rolesactive)) {
665 if (strpos($sql, ' '.$CFG->prefix.'user_students ') ||
666 strpos($sql, ' '.$CFG->prefix.'user_teachers ') ||
667 strpos($sql, ' '.$CFG->prefix.'user_coursecreators ') ||
668 strpos($sql, ' '.$CFG->prefix.'user_admins ')) {
669 if (debugging()) { var_dump(debug_backtrace()); }
670 error('This SQL relies on obsolete tables! Your code must be fixed by a developer.');
675 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
677 if ($limitfrom || $limitnum) {
678 ///Special case, 0 must be -1 for ADOdb
679 $limitfrom = empty($limitfrom) ? -1 : $limitfrom;
680 $limitnum = empty($limitnum) ? -1 : $limitnum;
681 $rs = $db->SelectLimit($sql, $limitnum, $limitfrom);
682 } else {
683 $rs = $db->Execute($sql);
685 if (!$rs) {
686 debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
687 if (!empty($CFG->dblogerror)) {
688 $debug=array_shift(debug_backtrace());
689 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql with limits ($limitfrom, $limitnum)");
691 return false;
694 return $rs;
698 * Utility function used by the following 4 methods. Note that for this to work, the first column
699 * in the recordset must contain unique values, as it is used as the key to the associative array.
701 * @param object an ADODB RecordSet object.
702 * @return mixed mixed an array of objects, or false if an error occured or the RecordSet was empty.
704 function recordset_to_array($rs) {
705 global $CFG;
707 $debugging = debugging('', DEBUG_DEVELOPER);
709 if ($rs && !rs_EOF($rs)) {
710 $objects = array();
711 /// First of all, we are going to get the name of the first column
712 /// to introduce it back after transforming the recordset to assoc array
713 /// See http://docs.moodle.org/en/XMLDB_Problems, fetch mode problem.
714 $firstcolumn = $rs->FetchField(0);
715 /// Get the whole associative array
716 if ($records = $rs->GetAssoc(true)) {
717 foreach ($records as $key => $record) {
718 /// Really DIRTY HACK for Oracle, but it's the only way to make it work
719 /// until we got all those NOT NULL DEFAULT '' out from Moodle
720 if ($CFG->dbfamily == 'oracle') {
721 array_walk($record, 'onespace2empty');
723 /// End of DIRTY HACK
724 $record[$firstcolumn->name] = $key;/// Re-add the assoc field
725 if ($debugging && array_key_exists($key, $objects)) {
726 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);
728 $objects[$key] = (object) $record; /// To object
730 return $objects;
731 /// Fallback in case we only have 1 field in the recordset. MDL-5877
732 } else if ($rs->_numOfFields == 1 && $records = $rs->GetRows()) {
733 foreach ($records as $key => $record) {
734 /// Really DIRTY HACK for Oracle, but it's the only way to make it work
735 /// until we got all those NOT NULL DEFAULT '' out from Moodle
736 if ($CFG->dbfamily == 'oracle') {
737 array_walk($record, 'onespace2empty');
739 /// End of DIRTY HACK
740 if ($debugging && array_key_exists($record[$firstcolumn->name], $objects)) {
741 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);
743 $objects[$record[$firstcolumn->name]] = (object) $record; /// The key is the first column value (like Assoc)
745 return $objects;
746 } else {
747 return false;
749 } else {
750 return false;
755 * This function is used to get the current record from the recordset. It
756 * doesn't advance the recordset position. You'll need to do that by
757 * using the rs_next_record($recordset) function.
758 * @param ADORecordSet the recordset to fetch current record from
759 * @return ADOFetchObj the object containing the fetched information
761 function rs_fetch_record(&$rs) {
762 global $CFG;
764 if (!$rs) {
765 debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
766 return false;
769 $rec = $rs->FetchObj(); //Retrieve record as object without advance the pointer
771 if ($rs->EOF) { //FetchObj requires manual checking of EOF to detect if it's the last record
772 $rec = false;
773 } else {
774 /// DIRTY HACK to retrieve all the ' ' (1 space) fields converted back
775 /// to '' (empty string) for Oracle. It's the only way to work with
776 /// all those NOT NULL DEFAULT '' fields until we definetively delete them
777 if ($CFG->dbfamily == 'oracle') {
778 $recarr = (array)$rec; /// Cast to array
779 array_walk($recarr, 'onespace2empty');
780 $rec = (object)$recarr;/// Cast back to object
782 /// End DIRTY HACK
785 return $rec;
789 * This function is used to advance the pointer of the recordset
790 * to its next position/record.
791 * @param ADORecordSet the recordset to be moved to the next record
792 * @return boolean true if the movement was successful and false if not (end of recordset)
794 function rs_next_record(&$rs) {
795 if (!$rs) {
796 debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
797 return false;
800 return $rs->MoveNext(); //Move the pointer to the next record
804 * This function is used to get the current record from the recordset. It
805 * does advance the recordset position.
806 * This is the prefered way to iterate over recordsets with code blocks like this:
808 * $rs = get_recordset('SELECT .....');
809 * while ($rec = rs_fetch_next_record($rs)) {
810 * /// Perform actions with the $rec record here
812 * rs_close($rs); /// Close the recordset if not used anymore. Saves memory (optional but recommended).
814 * @param ADORecordSet the recordset to fetch current record from
815 * @return mixed ADOFetchObj the object containing the fetched information or boolean false if no record (end of recordset)
817 function rs_fetch_next_record(&$rs) {
819 global $CFG;
821 if (!$rs) {
822 debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
823 return false;
826 $rec = false;
827 $recarr = $rs->FetchRow(); //Retrieve record as object without advance the pointer. It's quicker that FetchNextObj()
829 if ($recarr) {
830 /// DIRTY HACK to retrieve all the ' ' (1 space) fields converted back
831 /// to '' (empty string) for Oracle. It's the only way to work with
832 /// all those NOT NULL DEFAULT '' fields until we definetively delete them
833 if ($CFG->dbfamily == 'oracle') {
834 array_walk($recarr, 'onespace2empty');
836 /// End DIRTY HACK
837 /// Cast array to object
838 $rec = (object)$recarr;
841 return $rec;
845 * Returns true if no more records found
846 * @param ADORecordSet the recordset
847 * @return bool
849 function rs_EOF($rs) {
850 if (!$rs) {
851 debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
852 return true;
854 return $rs->EOF;
858 * This function closes the recordset, freeing all the memory and associated resources.
859 * Note that, once closed, the recordset must not be used anymore along the request.
860 * Saves memory (optional but recommended).
861 * @param ADORecordSet the recordset to be closed
862 * @return void
864 function rs_close(&$rs) {
865 if (!$rs) {
866 debugging('Incorrect $rs used!', DEBUG_DEVELOPER);
867 return;
870 $rs->Close();
874 * This function is used to convert all the Oracle 1-space defaults to the empty string
875 * like a really DIRTY HACK to allow it to work better until all those NOT NULL DEFAULT ''
876 * fields will be out from Moodle.
877 * @param string the string to be converted to '' (empty string) if it's ' ' (one space)
878 * @param mixed the key of the array in case we are using this function from array_walk,
879 * defaults to null for other (direct) uses
880 * @return boolean always true (the converted variable is returned by reference)
882 function onespace2empty(&$item, $key=null) {
883 $item = $item == ' ' ? '' : $item;
884 return true;
886 ///End DIRTY HACK
890 * Get a number of records as an array of objects.
892 * If the query succeeds and returns at least one record, the
893 * return value is an array of objects, one object for each
894 * record found. The array key is the value from the first
895 * column of the result set. The object associated with that key
896 * has a member variable for each column of the results.
898 * @param string $table the table to query.
899 * @param string $field a field to check (optional).
900 * @param string $value the value the field must have (requred if field1 is given, else optional).
901 * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
902 * @param string $fields a comma separated list of fields to return (optional, by default
903 * all fields are returned). The first field will be used as key for the
904 * array so must be a unique field such as 'id'.
905 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
906 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
907 * @return mixed an array of objects, or false if no records were found or an error occured.
909 function get_records($table, $field='', $value='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
910 $rs = get_recordset($table, $field, $value, $sort, $fields, $limitfrom, $limitnum);
911 return recordset_to_array($rs);
915 * Get a number of records as an array of objects.
917 * Return value as for @see function get_records.
919 * @param string $table the table to query.
920 * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
921 * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
922 * @param string $fields a comma separated list of fields to return
923 * (optional, by default all fields are returned). The first field will be used as key for the
924 * array so must be a unique field such as 'id'.
925 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
926 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
927 * @return mixed an array of objects, or false if no records were found or an error occured.
929 function get_records_select($table, $select='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
930 $rs = get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
931 return recordset_to_array($rs);
935 * Get a number of records as an array of objects.
937 * Return value as for @see function get_records.
939 * @param string $table The database table to be checked against.
940 * @param string $field The field to search
941 * @param string $values Comma separated list of possible value
942 * @param string $sort Sort order (as valid SQL sort parameter)
943 * @param string $fields A comma separated list of fields to be returned from the chosen table. If specified,
944 * the first field should be a unique one such as 'id' since it will be used as a key in the associative
945 * array.
946 * @return mixed an array of objects, or false if no records were found or an error occured.
948 function get_records_list($table, $field='', $values='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
949 $rs = get_recordset_list($table, $field, $values, $sort, $fields, $limitfrom, $limitnum);
950 return recordset_to_array($rs);
954 * Get a number of records as an array of objects.
956 * Return value as for @see function get_records.
958 * @param string $sql the SQL select query to execute. The first column of this SELECT statement
959 * must be a unique value (usually the 'id' field), as it will be used as the key of the
960 * returned array.
961 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
962 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
963 * @return mixed an array of objects, or false if no records were found or an error occured.
965 function get_records_sql($sql, $limitfrom='', $limitnum='') {
966 $rs = get_recordset_sql($sql, $limitfrom, $limitnum);
967 return recordset_to_array($rs);
971 * Utility function used by the following 3 methods.
973 * @param object an ADODB RecordSet object with two columns.
974 * @return mixed an associative array, or false if an error occured or the RecordSet was empty.
976 function recordset_to_menu($rs) {
977 global $CFG;
978 $menu = array();
979 if ($rs && !rs_EOF($rs)) {
980 $keys = array_keys($rs->fields);
981 $key0=$keys[0];
982 $key1=$keys[1];
983 while (!$rs->EOF) {
984 $menu[$rs->fields[$key0]] = $rs->fields[$key1];
985 $rs->MoveNext();
987 /// Really DIRTY HACK for Oracle, but it's the only way to make it work
988 /// until we got all those NOT NULL DEFAULT '' out from Moodle
989 if ($CFG->dbfamily == 'oracle') {
990 array_walk($menu, 'onespace2empty');
992 /// End of DIRTY HACK
993 return $menu;
994 } else {
995 return false;
1000 * Utility function
1001 * Similar to recordset_to_menu
1003 * field1, field2 is needed because the order from get_records_sql is not reliable
1004 * @param records - records from get_records_sql() or get_records()
1005 * @param field1 - field to be used as menu index
1006 * @param field2 - feild to be used as coresponding menu value
1007 * @return mixed an associative array, or false if an error occured or the RecordSet was empty.
1009 function records_to_menu($records, $field1, $field2) {
1011 $menu = array();
1012 foreach ($records as $record) {
1013 $menu[$record->$field1] = $record->$field2;
1016 if (!empty($menu)) {
1017 return $menu;
1018 } else {
1019 return false;
1024 * Get the first two columns from a number of records as an associative array.
1026 * Arguments as for @see function get_recordset.
1028 * If no errors occur, and at least one records is found, the return value
1029 * is an associative whose keys come from the first field of each record,
1030 * and whose values are the corresponding second fields. If no records are found,
1031 * or an error occurs, false is returned.
1033 * @param string $table the table to query.
1034 * @param string $field a field to check (optional).
1035 * @param string $value the value the field must have (requred if field1 is given, else optional).
1036 * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter).
1037 * @param string $fields a comma separated list of fields to return (optional, by default all fields are returned).
1038 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
1039 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
1040 * @return mixed an associative array, or false if no records were found or an error occured.
1042 function get_records_menu($table, $field='', $value='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
1043 $rs = get_recordset($table, $field, $value, $sort, $fields, $limitfrom, $limitnum);
1044 return recordset_to_menu($rs);
1048 * Get the first two columns from a number of records as an associative array.
1050 * Arguments as for @see function get_recordset_select.
1051 * Return value as for @see function get_records_menu.
1053 * @param string $table The database table to be checked against.
1054 * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
1055 * @param string $sort Sort order (optional) - a valid SQL order parameter
1056 * @param string $fields A comma separated list of fields to be returned from the chosen table.
1057 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
1058 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
1059 * @return mixed an associative array, or false if no records were found or an error occured.
1061 function get_records_select_menu($table, $select='', $sort='', $fields='*', $limitfrom='', $limitnum='') {
1062 $rs = get_recordset_select($table, $select, $sort, $fields, $limitfrom, $limitnum);
1063 return recordset_to_menu($rs);
1067 * Get the first two columns from a number of records as an associative array.
1069 * Arguments as for @see function get_recordset_sql.
1070 * Return value as for @see function get_records_menu.
1072 * @param string $sql The SQL string you wish to be executed.
1073 * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
1074 * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
1075 * @return mixed an associative array, or false if no records were found or an error occured.
1077 function get_records_sql_menu($sql, $limitfrom='', $limitnum='') {
1078 $rs = get_recordset_sql($sql, $limitfrom, $limitnum);
1079 return recordset_to_menu($rs);
1083 * Get a single value from a table row where all the given fields match the given values.
1085 * @param string $table the table to query.
1086 * @param string $return the field to return the value of.
1087 * @param string $field1 the first field to check (optional).
1088 * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
1089 * @param string $field2 the second field to check (optional).
1090 * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
1091 * @param string $field3 the third field to check (optional).
1092 * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
1093 * @return mixed the specified value, or false if an error occured.
1095 function get_field($table, $return, $field1, $value1, $field2='', $value2='', $field3='', $value3='') {
1096 global $CFG;
1097 $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
1098 return get_field_sql('SELECT ' . $return . ' FROM ' . $CFG->prefix . $table . ' ' . $select);
1102 * Get a single value from a table row where a particular select clause is true.
1104 * @uses $CFG
1105 * @param string $table the table to query.
1106 * @param string $return the field to return the value of.
1107 * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
1108 * @return mixed the specified value, or false if an error occured.
1110 function get_field_select($table, $return, $select) {
1111 global $CFG;
1112 if ($select) {
1113 $select = 'WHERE '. $select;
1115 return get_field_sql('SELECT ' . $return . ' FROM ' . $CFG->prefix . $table . ' ' . $select);
1119 * Get a single value from a table.
1121 * @param string $sql an SQL statement expected to return a single value.
1122 * @return mixed the specified value, or false if an error occured.
1124 function get_field_sql($sql) {
1125 global $CFG;
1127 /// Strip potential LIMIT uses arriving here, debugging them (MDL-7173)
1128 $newsql = preg_replace('/ LIMIT [0-9, ]+$/is', '', $sql);
1129 if ($newsql != $sql) {
1130 debugging('Incorrect use of LIMIT clause (not cross-db) in call to get_field_sql(): ' . s($sql), DEBUG_DEVELOPER);
1131 $sql = $newsql;
1134 $rs = get_recordset_sql($sql, 0, 1);
1136 if ($rs && $rs->RecordCount() == 1) {
1137 /// DIRTY HACK to retrieve all the ' ' (1 space) fields converted back
1138 /// to '' (empty string) for Oracle. It's the only way to work with
1139 /// all those NOT NULL DEFAULT '' fields until we definetively delete them
1140 if ($CFG->dbfamily == 'oracle') {
1141 $value = reset($rs->fields);
1142 onespace2empty($value);
1143 return $value;
1145 /// End of DIRTY HACK
1146 return reset($rs->fields);
1147 } else {
1148 return false;
1153 * Get a single value from a table row where a particular select clause is true.
1155 * @uses $CFG
1156 * @param string $table the table to query.
1157 * @param string $return the field to return the value of.
1158 * @param string $select A fragment of SQL to be used in a where clause in the SQL call.
1159 * @return mixed|false Returns the value return from the SQL statment or false if an error occured.
1161 function get_fieldset_select($table, $return, $select) {
1162 global $CFG;
1163 if ($select) {
1164 $select = ' WHERE '. $select;
1166 return get_fieldset_sql('SELECT ' . $return . ' FROM ' . $CFG->prefix . $table . $select);
1170 * Get an array of data from one or more fields from a database
1171 * use to get a column, or a series of distinct values
1173 * @uses $CFG
1174 * @uses $db
1175 * @param string $sql The SQL string you wish to be executed.
1176 * @return mixed|false Returns the value return from the SQL statment or false if an error occured.
1177 * @todo Finish documenting this function
1179 function get_fieldset_sql($sql) {
1181 global $db, $CFG;
1183 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
1185 $rs = $db->Execute($sql);
1186 if (!$rs) {
1187 debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
1188 if (!empty($CFG->dblogerror)) {
1189 $debug=array_shift(debug_backtrace());
1190 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
1192 return false;
1195 if ( !rs_EOF($rs) ) {
1196 $keys = array_keys($rs->fields);
1197 $key0 = $keys[0];
1198 $results = array();
1199 while (!$rs->EOF) {
1200 array_push($results, $rs->fields[$key0]);
1201 $rs->MoveNext();
1203 /// DIRTY HACK to retrieve all the ' ' (1 space) fields converted back
1204 /// to '' (empty string) for Oracle. It's the only way to work with
1205 /// all those NOT NULL DEFAULT '' fields until we definetively delete them
1206 if ($CFG->dbfamily == 'oracle') {
1207 array_walk($results, 'onespace2empty');
1209 /// End of DIRTY HACK
1210 rs_close($rs);
1211 return $results;
1212 } else {
1213 rs_close($rs);
1214 return false;
1219 * Set a single field in every table row where all the given fields match the given values.
1221 * @uses $CFG
1222 * @uses $db
1223 * @param string $table The database table to be checked against.
1224 * @param string $newfield the field to set.
1225 * @param string $newvalue the value to set the field to.
1226 * @param string $field1 the first field to check (optional).
1227 * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
1228 * @param string $field2 the second field to check (optional).
1229 * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
1230 * @param string $field3 the third field to check (optional).
1231 * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
1232 * @return mixed An ADODB RecordSet object with the results from the SQL call or false.
1234 function set_field($table, $newfield, $newvalue, $field1, $value1, $field2='', $value2='', $field3='', $value3='') {
1236 global $CFG;
1238 // Clear record_cache based on the parameters passed
1239 // (individual record or whole table)
1240 if ($CFG->rcache === true) {
1241 if ($field1 == 'id') {
1242 rcache_unset($table, $value1);
1243 } else if ($field2 == 'id') {
1244 rcache_unset($table, $value2);
1245 } else if ($field3 == 'id') {
1246 rcache_unset($table, $value3);
1247 } else {
1248 rcache_unset_table($table);
1252 $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
1254 return set_field_select($table, $newfield, $newvalue, $select, true);
1258 * Set a single field in every table row where the select statement evaluates to true.
1260 * @uses $CFG
1261 * @uses $db
1262 * @param string $table The database table to be checked against.
1263 * @param string $newfield the field to set.
1264 * @param string $newvalue the value to set the field to.
1265 * @param string $select a fragment of SQL to be used in a where clause in the SQL call.
1266 * @param boolean $localcall Leave this set to false. (Should only be set to true by set_field.)
1267 * @return mixed An ADODB RecordSet object with the results from the SQL call or false.
1269 function set_field_select($table, $newfield, $newvalue, $select, $localcall = false) {
1271 global $db, $CFG;
1273 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
1275 if (!$localcall) {
1276 if ($select) {
1277 $select = 'WHERE ' . $select;
1280 // Clear record_cache based on the parameters passed
1281 // (individual record or whole table)
1282 if ($CFG->rcache === true) {
1283 rcache_unset_table($table);
1287 $dataobject = new StdClass;
1288 $dataobject->{$newfield} = $newvalue;
1289 // Oracle DIRTY HACK -
1290 if ($CFG->dbfamily == 'oracle') {
1291 oracle_dirty_hack($table, $dataobject); // Convert object to the correct "empty" values for Oracle DB
1292 $newvalue = $dataobject->{$newfield};
1294 // End DIRTY HACK
1296 /// Under Oracle, MSSQL and PostgreSQL we have our own set field process
1297 /// If the field being updated is clob/blob, we use our alternate update here
1298 /// They will be updated later
1299 if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') && !empty($select)) {
1300 /// Detect lobs
1301 $foundclobs = array();
1302 $foundblobs = array();
1303 db_detect_lobs($table, $dataobject, $foundclobs, $foundblobs);
1306 /// Under Oracle, MSSQL and PostgreSQL, finally, update all the Clobs and Blobs present in the record
1307 /// if we know we have some of them in the query
1308 if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') && !empty($select) &&
1309 (!empty($foundclobs) || !empty($foundblobs))) {
1310 if (!db_update_lobs($table, $select, $foundclobs, $foundblobs)) {
1311 return false; //Some error happened while updating LOBs
1312 } else {
1313 return true; //Everrything was ok
1317 /// NULL inserts - introduced in 1.9
1318 if (is_null($newvalue)) {
1319 $update = "$newfield = NULL";
1320 } else {
1321 $update = "$newfield = '$newvalue'";
1324 /// Arriving here, standard update
1325 $sql = 'UPDATE '. $CFG->prefix . $table .' SET '.$update.' '.$select;
1326 $rs = $db->Execute($sql);
1327 if (!$rs) {
1328 debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
1329 if (!empty($CFG->dblogerror)) {
1330 $debug=array_shift(debug_backtrace());
1331 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
1333 return false;
1335 return $rs;
1339 * Delete the records from a table where all the given fields match the given values.
1341 * @uses $CFG
1342 * @uses $db
1343 * @param string $table the table to delete from.
1344 * @param string $field1 the first field to check (optional).
1345 * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
1346 * @param string $field2 the second field to check (optional).
1347 * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
1348 * @param string $field3 the third field to check (optional).
1349 * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
1350 * @return mixed An ADODB RecordSet object with the results from the SQL call or false.
1352 function delete_records($table, $field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
1354 global $db, $CFG;
1356 // Clear record_cache based on the parameters passed
1357 // (individual record or whole table)
1358 if ($CFG->rcache === true) {
1359 if ($field1 == 'id') {
1360 rcache_unset($table, $value1);
1361 } else if ($field2 == 'id') {
1362 rcache_unset($table, $value2);
1363 } else if ($field3 == 'id') {
1364 rcache_unset($table, $value3);
1365 } else {
1366 rcache_unset_table($table);
1370 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
1372 $select = where_clause($field1, $value1, $field2, $value2, $field3, $value3);
1374 $sql = 'DELETE FROM '. $CFG->prefix . $table .' '. $select;
1375 $rs = $db->Execute($sql);
1376 if (!$rs) {
1377 debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
1378 if (!empty($CFG->dblogerror)) {
1379 $debug=array_shift(debug_backtrace());
1380 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
1382 return false;
1384 return $rs;
1388 * Delete one or more records from a table
1390 * @uses $CFG
1391 * @uses $db
1392 * @param string $table The database table to be checked against.
1393 * @param string $select A fragment of SQL to be used in a where clause in the SQL call (used to define the selection criteria).
1394 * @return object A PHP standard object with the results from the SQL call.
1395 * @todo Verify return type.
1397 function delete_records_select($table, $select='') {
1399 global $CFG, $db;
1401 // Clear record_cache (whole table)
1402 if ($CFG->rcache === true) {
1403 rcache_unset_table($table);
1406 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
1408 if ($select) {
1409 $select = 'WHERE '.$select;
1412 $sql = 'DELETE FROM '. $CFG->prefix . $table .' '. $select;
1413 $rs = $db->Execute($sql);
1414 if (!$rs) {
1415 debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
1416 if (!empty($CFG->dblogerror)) {
1417 $debug=array_shift(debug_backtrace());
1418 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
1420 return false;
1422 return $rs;
1426 * Insert a record into a table and return the "id" field if required
1428 * If the return ID isn't required, then this just reports success as true/false.
1429 * $dataobject is an object containing needed data
1431 * @uses $db
1432 * @uses $CFG
1433 * @param string $table The database table to be checked against.
1434 * @param object $dataobject A data object with values for one or more fields in the record
1435 * @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.
1436 * @param string $primarykey (obsolete) This is now forced to be 'id'.
1438 function insert_record($table, $dataobject, $returnid=true, $primarykey='id') {
1440 global $db, $CFG, $empty_rs_cache;
1442 if (empty($db)) {
1443 return false;
1446 /// Check we are handling a proper $dataobject
1447 if (is_array($dataobject)) {
1448 debugging('Warning. Wrong call to insert_record(). $dataobject must be an object. array found instead', DEBUG_DEVELOPER);
1449 $dataobject = (object)$dataobject;
1452 /// Temporary hack as part of phasing out all access to obsolete user tables XXX
1453 if (!empty($CFG->rolesactive)) {
1454 if (in_array($table, array('user_students', 'user_teachers', 'user_coursecreators', 'user_admins'))) {
1455 if (debugging()) { var_dump(debug_backtrace()); }
1456 error('This SQL relies on obsolete tables ('.$table.')! Your code must be fixed by a developer.');
1460 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
1462 /// In Moodle we always use auto-numbering fields for the primary key
1463 /// so let's unset it now before it causes any trouble later
1464 unset($dataobject->{$primarykey});
1466 /// Get an empty recordset. Cache for multiple inserts.
1467 if (empty($empty_rs_cache[$table])) {
1468 /// Execute a dummy query to get an empty recordset
1469 if (!$empty_rs_cache[$table] = $db->Execute('SELECT * FROM '. $CFG->prefix . $table .' WHERE '. $primarykey .' = \'-1\'')) {
1470 return false;
1474 $rs = $empty_rs_cache[$table];
1476 /// Postgres doesn't have the concept of primary key built in
1477 /// and will return the OID which isn't what we want.
1478 /// The efficient and transaction-safe strategy is to
1479 /// move the sequence forward first, and make the insert
1480 /// with an explicit id.
1481 if ( $CFG->dbfamily === 'postgres' && $returnid == true ) {
1482 if ($nextval = (int)get_field_sql("SELECT NEXTVAL('{$CFG->prefix}{$table}_{$primarykey}_seq')")) {
1483 $dataobject->{$primarykey} = $nextval;
1487 /// Begin DIRTY HACK
1488 if ($CFG->dbfamily == 'oracle') {
1489 oracle_dirty_hack($table, $dataobject); // Convert object to the correct "empty" values for Oracle DB
1491 /// End DIRTY HACK
1493 /// Under Oracle, MSSQL and PostgreSQL we have our own insert record process
1494 /// detect all the clob/blob fields and change their contents to @#CLOB#@ and @#BLOB#@
1495 /// saving them into $foundclobs and $foundblobs [$fieldname]->contents
1496 /// Same for mssql (only processing blobs - image fields)
1497 if ($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') {
1498 $foundclobs = array();
1499 $foundblobs = array();
1500 db_detect_lobs($table, $dataobject, $foundclobs, $foundblobs);
1503 /// Under Oracle, if the primary key inserted has been requested OR
1504 /// if there are LOBs to insert, we calculate the next value via
1505 /// explicit query to the sequence.
1506 /// Else, the pre-insert trigger will do the job, because the primary
1507 /// key isn't needed at all by the rest of PHP code
1508 if ($CFG->dbfamily === 'oracle' && ($returnid == true || !empty($foundclobs) || !empty($foundblobs))) {
1509 /// We need this here (move this function to dmlib?)
1510 include_once($CFG->libdir . '/ddllib.php');
1511 $xmldb_table = new XMLDBTable($table);
1512 $seqname = find_sequence_name($xmldb_table);
1513 if (!$seqname) {
1514 /// Fallback, seqname not found, something is wrong. Inform and use the alternative getNameForObject() method
1515 debugging('Sequence name for table ' . $table->getName() . ' not found', DEBUG_DEVELOPER);
1516 $generator = new XMLDBoci8po();
1517 $generator->setPrefix($CFG->prefix);
1518 $seqname = $generator->getNameForObject($table, $primarykey, 'seq');
1520 if ($nextval = (int)$db->GenID($seqname)) {
1521 $dataobject->{$primarykey} = $nextval;
1522 } else {
1523 debugging('Not able to get value from sequence ' . $seqname, DEBUG_DEVELOPER);
1527 /// Get the correct SQL from adoDB
1528 if (!$insertSQL = $db->GetInsertSQL($rs, (array)$dataobject, true)) {
1529 return false;
1532 /// Under Oracle, MSSQL and PostgreSQL, replace all the '@#CLOB#@' and '@#BLOB#@' ocurrences to proper default values
1533 /// if we know we have some of them in the query
1534 if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') &&
1535 (!empty($foundclobs) || !empty($foundblobs))) {
1536 /// Initial configuration, based on DB
1537 switch ($CFG->dbfamily) {
1538 case 'oracle':
1539 $clobdefault = 'empty_clob()'; //Value of empty default clobs for this DB
1540 $blobdefault = 'empty_blob()'; //Value of empty default blobs for this DB
1541 break;
1542 case 'mssql':
1543 case 'postgres':
1544 $clobdefault = 'null'; //Value of empty default clobs for this DB (under mssql this won't be executed
1545 $blobdefault = 'null'; //Value of empty default blobs for this DB
1546 break;
1548 $insertSQL = str_replace("'@#CLOB#@'", $clobdefault, $insertSQL);
1549 $insertSQL = str_replace("'@#BLOB#@'", $blobdefault, $insertSQL);
1552 /// Run the SQL statement
1553 if (!$rs = $db->Execute($insertSQL)) {
1554 debugging($db->ErrorMsg() .'<br /><br />'.s($insertSQL));
1555 if (!empty($CFG->dblogerror)) {
1556 $debug=array_shift(debug_backtrace());
1557 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $insertSQL");
1559 return false;
1562 /// Under Oracle and PostgreSQL, finally, update all the Clobs and Blobs present in the record
1563 /// if we know we have some of them in the query
1564 if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'postgres') &&
1565 !empty($dataobject->{$primarykey}) &&
1566 (!empty($foundclobs) || !empty($foundblobs))) {
1567 if (!db_update_lobs($table, $dataobject->{$primarykey}, $foundclobs, $foundblobs)) {
1568 return false; //Some error happened while updating LOBs
1572 /// If a return ID is not needed then just return true now (but not in MSSQL DBs, where we may have some pending tasks)
1573 if (!$returnid && $CFG->dbfamily != 'mssql') {
1574 return true;
1577 /// We already know the record PK if it's been passed explicitly,
1578 /// or if we've retrieved it from a sequence (Postgres and Oracle).
1579 if (!empty($dataobject->{$primarykey})) {
1580 return $dataobject->{$primarykey};
1583 /// This only gets triggered with MySQL and MSQL databases
1584 /// however we have some postgres fallback in case we failed
1585 /// to find the sequence.
1586 $id = $db->Insert_ID();
1588 /// Under MSSQL all the Clobs and Blobs (IMAGE) present in the record
1589 /// if we know we have some of them in the query
1590 if (($CFG->dbfamily == 'mssql') &&
1591 !empty($id) &&
1592 (!empty($foundclobs) || !empty($foundblobs))) {
1593 if (!db_update_lobs($table, $id, $foundclobs, $foundblobs)) {
1594 return false; //Some error happened while updating LOBs
1598 if ($CFG->dbfamily === 'postgres') {
1599 // try to get the primary key based on id
1600 if ( ($rs = $db->Execute('SELECT '. $primarykey .' FROM '. $CFG->prefix . $table .' WHERE oid = '. $id))
1601 && ($rs->RecordCount() == 1) ) {
1602 trigger_error("Retrieved $primarykey from oid on table $table because we could not find the sequence.");
1603 return (integer)reset($rs->fields);
1605 trigger_error('Failed to retrieve primary key after insert: SELECT '. $primarykey .
1606 ' FROM '. $CFG->prefix . $table .' WHERE oid = '. $id);
1607 return false;
1610 return (integer)$id;
1614 * Update a record in a table
1616 * $dataobject is an object containing needed data
1617 * Relies on $dataobject having a variable "id" to
1618 * specify the record to update
1620 * @uses $CFG
1621 * @uses $db
1622 * @param string $table The database table to be checked against.
1623 * @param object $dataobject An object with contents equal to fieldname=>fieldvalue. Must have an entry for 'id' to map to the table specified.
1624 * @return bool
1626 function update_record($table, $dataobject) {
1628 global $db, $CFG;
1630 // integer value in id propery required
1631 if (empty($dataobject->id)) {
1632 return false;
1634 $dataobject->id = (int)$dataobject->id;
1636 /// Check we are handling a proper $dataobject
1637 if (is_array($dataobject)) {
1638 debugging('Warning. Wrong call to update_record(). $dataobject must be an object. array found instead', DEBUG_DEVELOPER);
1639 $dataobject = (object)$dataobject;
1642 // Remove this record from record cache since it will change
1643 if (!empty($CFG->rcache)) { // no === here! breaks upgrade
1644 rcache_unset($table, $dataobject->id);
1647 /// Temporary hack as part of phasing out all access to obsolete user tables XXX
1648 if (!empty($CFG->rolesactive)) {
1649 if (in_array($table, array('user_students', 'user_teachers', 'user_coursecreators', 'user_admins'))) {
1650 if (debugging()) { var_dump(debug_backtrace()); }
1651 error('This SQL relies on obsolete tables ('.$table.')! Your code must be fixed by a developer.');
1655 /// Begin DIRTY HACK
1656 if ($CFG->dbfamily == 'oracle') {
1657 oracle_dirty_hack($table, $dataobject); // Convert object to the correct "empty" values for Oracle DB
1659 /// End DIRTY HACK
1661 /// Under Oracle, MSSQL and PostgreSQL we have our own update record process
1662 /// detect all the clob/blob fields and delete them from the record being updated
1663 /// saving them into $foundclobs and $foundblobs [$fieldname]->contents
1664 /// They will be updated later
1665 if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres')
1666 && !empty($dataobject->id)) {
1667 /// Detect lobs
1668 $foundclobs = array();
1669 $foundblobs = array();
1670 db_detect_lobs($table, $dataobject, $foundclobs, $foundblobs, true);
1673 // Determine all the fields in the table
1674 if (!$columns = $db->MetaColumns($CFG->prefix . $table)) {
1675 return false;
1677 $data = (array)$dataobject;
1679 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
1681 // Pull out data matching these fields
1682 $update = array();
1683 foreach ($columns as $column) {
1684 if ($column->name == 'id') {
1685 continue;
1687 if (array_key_exists($column->name, $data)) {
1688 $key = $column->name;
1689 $value = $data[$key];
1690 if (is_null($value)) {
1691 $update[] = "$key = NULL"; // previously NULLs were not updated
1692 } else if (is_bool($value)) {
1693 $value = (int)$value;
1694 $update[] = "$key = $value"; // lets keep pg happy, '' is not correct smallint MDL-13038
1695 } else {
1696 $update[] = "$key = '$value'"; // All incoming data is already quoted
1701 /// Only if we have fields to be updated (this will prevent both wrong updates +
1702 /// updates of only LOBs in Oracle
1703 if ($update) {
1704 $query = "UPDATE {$CFG->prefix}{$table} SET ".implode(',', $update)." WHERE id = {$dataobject->id}";
1705 if (!$rs = $db->Execute($query)) {
1706 debugging($db->ErrorMsg() .'<br /><br />'.s($query));
1707 if (!empty($CFG->dblogerror)) {
1708 $debug=array_shift(debug_backtrace());
1709 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $query");
1711 return false;
1715 /// Under Oracle, MSSQL and PostgreSQL, finally, update all the Clobs and Blobs present in the record
1716 /// if we know we have some of them in the query
1717 if (($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') &&
1718 !empty($dataobject->id) &&
1719 (!empty($foundclobs) || !empty($foundblobs))) {
1720 if (!db_update_lobs($table, $dataobject->id, $foundclobs, $foundblobs)) {
1721 return false; //Some error happened while updating LOBs
1725 return true;
1731 * Returns the proper SQL to do paging
1733 * @uses $CFG
1734 * @param string $page Offset page number
1735 * @param string $recordsperpage Number of records per page
1736 * @deprecated Moodle 1.7 use the new $limitfrom, $limitnum available in all
1737 * the get_recordXXX() funcions.
1738 * @return string
1740 function sql_paging_limit($page, $recordsperpage) {
1741 global $CFG;
1743 debugging('Function sql_paging_limit() is deprecated. Replace it with the correct use of limitfrom, limitnum parameters', DEBUG_DEVELOPER);
1745 switch ($CFG->dbfamily) {
1746 case 'postgres':
1747 return 'LIMIT '. $recordsperpage .' OFFSET '. $page;
1748 default:
1749 return 'LIMIT '. $page .','. $recordsperpage;
1754 * Returns the proper SQL to do LIKE in a case-insensitive way
1756 * Note the LIKE are case sensitive for Oracle. Oracle 10g is required to use
1757 * the caseinsensitive search using regexp_like() or NLS_COMP=LINGUISTIC :-(
1758 * See http://docs.moodle.org/en/XMLDB_Problems#Case-insensitive_searches
1760 * @uses $CFG
1761 * @return string
1763 function sql_ilike() {
1764 global $CFG;
1766 switch ($CFG->dbfamily) {
1767 case 'postgres':
1768 return 'ILIKE';
1769 default:
1770 return 'LIKE';
1776 * Returns the proper SQL to do MAX
1778 * @uses $CFG
1779 * @param string $field
1780 * @return string
1782 function sql_max($field) {
1783 global $CFG;
1785 switch ($CFG->dbfamily) {
1786 default:
1787 return "MAX($field)";
1792 * Returns the proper SQL (for the dbms in use) to concatenate $firstname and $lastname
1794 * @uses $CFG
1795 * @param string $firstname User's first name
1796 * @param string $lastname User's last name
1797 * @return string
1799 function sql_fullname($firstname='firstname', $lastname='lastname') {
1800 return sql_concat($firstname, "' '", $lastname);
1804 * Returns the proper SQL to do CONCAT between the elements passed
1805 * Can take many parameters - just a passthrough to $db->Concat()
1807 * @uses $db
1808 * @param string $element
1809 * @return string
1811 function sql_concat() {
1812 global $db, $CFG;
1814 $args = func_get_args();
1815 /// PostgreSQL requires at least one char element in the concat, let's add it
1816 /// here (at the beginning of the array) until ADOdb fixes it
1817 if ($CFG->dbfamily == 'postgres' && is_array($args)) {
1818 array_unshift($args , "''");
1820 return call_user_func_array(array($db, 'Concat'), $args);
1824 * Returns the proper SQL to do CONCAT between the elements passed
1825 * with a given separator
1827 * @uses $db
1828 * @param string $separator
1829 * @param array $elements
1830 * @return string
1832 function sql_concat_join($separator="' '", $elements=array()) {
1833 global $db;
1835 // copy to ensure pass by value
1836 $elem = $elements;
1838 // Intersperse $elements in the array.
1839 // Add items to the array on the fly, walking it
1840 // _backwards_ splicing the elements in. The loop definition
1841 // should skip first and last positions.
1842 for ($n=count($elem)-1; $n > 0 ; $n--) {
1843 array_splice($elem, $n, 0, $separator);
1845 return call_user_func_array(array($db, 'Concat'), $elem);
1849 * Returns the proper SQL to know if one field is empty.
1851 * Note that the function behavior strongly relies on the
1852 * parameters passed describing the field so, please, be accurate
1853 * when speciffying them.
1855 * Also, note that this function is not suitable to look for
1856 * fields having NULL contents at all. It's all for empty values!
1858 * This function should be applied in all the places where conditins of
1859 * the type:
1861 * ... AND fieldname = '';
1863 * are being used. Final result should be:
1865 * ... AND ' . sql_isempty('tablename', 'fieldname', true/false, true/false);
1867 * (see parameters description below)
1869 * @param string $tablename name of the table (without prefix). Not used for now but can be
1870 * necessary in the future if we want to use some introspection using
1871 * meta information against the DB. /// TODO ///
1872 * @param string $fieldname name of the field we are going to check
1873 * @param boolean $nullablefield to specify if the field us nullable (true) or no (false) in the DB
1874 * @param boolean $textfield to specify if it is a text (also called clob) field (true) or a varchar one (false)
1875 * @return string the sql code to be added to check for empty values
1877 function sql_isempty($tablename, $fieldname, $nullablefield, $textfield) {
1879 global $CFG;
1881 $sql = $fieldname . " = ''";
1883 switch ($CFG->dbfamily) {
1884 case 'mssql':
1885 if ($textfield) {
1886 $sql = sql_compare_text($fieldname) . " = ''";
1888 break;
1889 case 'oracle':
1890 if ($nullablefield) {
1891 $sql = $fieldname . " IS NULL"; /// empties in nullable fields are stored as
1892 } else { /// NULLs
1893 if ($textfield) {
1894 $sql = sql_compare_text($fieldname) . " = ' '"; /// oracle_dirty_hack inserts 1-whitespace
1895 } else { /// in NOT NULL varchar and text columns so
1896 $sql = $fieldname . " = ' '"; /// we need to look for that in any situation
1899 break;
1902 return ' ' . $sql . ' '; /// Adding spaces to avoid wrong SQLs due to concatenation
1906 * Returns the proper SQL to know if one field is not empty.
1908 * Note that the function behavior strongly relies on the
1909 * parameters passed describing the field so, please, be accurate
1910 * when speciffying them.
1912 * This function should be applied in all the places where conditions of
1913 * the type:
1915 * ... AND fieldname != '';
1917 * are being used. Final result should be:
1919 * ... AND ' . sql_isnotempty('tablename', 'fieldname', true/false, true/false);
1921 * (see parameters description below)
1923 * @param string $tablename name of the table (without prefix). Not used for now but can be
1924 * necessary in the future if we want to use some introspection using
1925 * meta information against the DB. /// TODO ///
1926 * @param string $fieldname name of the field we are going to check
1927 * @param boolean $nullablefield to specify if the field us nullable (true) or no (false) in the DB
1928 * @param boolean $textfield to specify if it is a text (also called clob) field (true) or a varchar one (false)
1929 * @return string the sql code to be added to check for non empty values
1931 function sql_isnotempty($tablename, $fieldname, $nullablefield, $textfield) {
1933 return ' ( NOT ' . sql_isempty($tablename, $fieldname, $nullablefield, $textfield) . ') ';
1937 * Returns the proper AS keyword to be used to aliase columns
1938 * SQL defines the keyword as optional and nobody but PG
1939 * seems to require it. This function should be used inside all
1940 * the statements using column aliases.
1941 * Note than the use of table aliases doesn't require the
1942 * AS keyword at all, only columns for postgres.
1943 * @uses $CFG
1944 * @ return string the keyword
1945 * @deprecated Moodle 1.7 because coding guidelines now enforce to use AS in column aliases
1947 function sql_as() {
1948 global $CFG, $db;
1950 switch ($CFG->dbfamily) {
1951 case 'postgres':
1952 return 'AS';
1953 default:
1954 return '';
1959 * Returns the empty string char used by every supported DB. To be used when
1960 * we are searching for that values in our queries. Only Oracle uses this
1961 * for now (will be out, once we migrate to proper NULLs if that days arrives)
1963 function sql_empty() {
1964 global $CFG;
1966 switch ($CFG->dbfamily) {
1967 case 'oracle':
1968 return ' '; //Only Oracle uses 1 white-space
1969 default:
1970 return '';
1975 * Returns the proper substr() function for each DB
1976 * Relies on ADOdb $db->substr property
1978 function sql_substr() {
1980 global $db;
1982 return $db->substr;
1986 * Returns the SQL text to be used to compare one TEXT (clob) column with
1987 * one varchar column, because some RDBMS doesn't support such direct
1988 * comparisons.
1989 * @param string fieldname the name of the TEXT field we need to order by
1990 * @param string number of chars to use for the ordering (defaults to 32)
1991 * @return string the piece of SQL code to be used in your statement.
1993 function sql_compare_text($fieldname, $numchars=32) {
1994 return sql_order_by_text($fieldname, $numchars);
1999 * Returns the SQL text to be used to order by one TEXT (clob) column, because
2000 * some RDBMS doesn't support direct ordering of such fields.
2001 * Note that the use or queries being ordered by TEXT columns must be minimised,
2002 * because it's really slooooooow.
2003 * @param string fieldname the name of the TEXT field we need to order by
2004 * @param string number of chars to use for the ordering (defaults to 32)
2005 * @return string the piece of SQL code to be used in your statement.
2007 function sql_order_by_text($fieldname, $numchars=32) {
2009 global $CFG;
2011 switch ($CFG->dbfamily) {
2012 case 'mssql':
2013 return 'CONVERT(varchar, ' . $fieldname . ', ' . $numchars . ')';
2014 break;
2015 case 'oracle':
2016 return 'dbms_lob.substr(' . $fieldname . ', ' . $numchars . ',1)';
2017 break;
2018 default:
2019 return $fieldname;
2024 * Returns the SQL text to be used to calculate the length in characters of one expression.
2025 * @param string fieldname or expression to calculate its length in characters.
2026 * @return string the piece of SQL code to be used in the statement.
2028 function sql_length($fieldname) {
2030 global $CFG;
2032 switch ($CFG->dbfamily) {
2033 case 'mysql':
2034 return 'CHAR_LENGTH(' . $fieldname . ')';
2035 break;
2036 case 'mssql':
2037 return 'LEN(' . $fieldname . ')';
2038 break;
2039 default:
2040 return 'LENGTH(' . $fieldname . ')';
2045 * Returns the SQL for returning searching one string for the location of another.
2046 * @param string $needle the SQL expression that will be searched for.
2047 * @param string $haystack the SQL expression that will be searched in.
2048 * @return string the required SQL
2050 function sql_position($needle, $haystack) {
2051 global $CFG;
2053 switch ($CFG->dbfamily) {
2054 case 'mssql':
2055 return "CHARINDEX(($needle), ($haystack))";
2056 break;
2057 case 'oracle':
2058 return "INSTR(($haystack), ($needle))";
2059 break;
2060 default:
2061 return "POSITION(($needle) IN ($haystack))";
2066 * Returns the SQL to be used in order to CAST one CHAR column to INTEGER.
2068 * Be aware that the CHAR column you're trying to cast contains really
2069 * int values or the RDBMS will throw an error!
2071 * @param string fieldname the name of the field to be casted
2072 * @param boolean text to specify if the original column is one TEXT (CLOB) column (true). Defaults to false.
2073 * @return string the piece of SQL code to be used in your statement.
2075 function sql_cast_char2int($fieldname, $text=false) {
2077 global $CFG;
2079 $sql = '';
2081 switch ($CFG->dbfamily) {
2082 case 'mysql':
2083 $sql = ' CAST(' . $fieldname . ' AS SIGNED) ';
2084 break;
2085 case 'postgres':
2086 $sql = ' CAST(' . $fieldname . ' AS INT) ';
2087 break;
2088 case 'mssql':
2089 if (!$text) {
2090 $sql = ' CAST(' . $fieldname . ' AS INT) ';
2091 } else {
2092 $sql = ' CAST(' . sql_compare_text($fieldname) . ' AS INT) ';
2094 break;
2095 case 'oracle':
2096 if (!$text) {
2097 $sql = ' CAST(' . $fieldname . ' AS INT) ';
2098 } else {
2099 $sql = ' CAST(' . sql_compare_text($fieldname) . ' AS INT) ';
2101 break;
2102 default:
2103 $sql = ' ' . $fieldname . ' ';
2106 return $sql;
2110 * Returns the SQL text to be used in order to perform one bitwise AND operation
2111 * between 2 integers.
2112 * @param integer int1 first integer in the operation
2113 * @param integer int2 second integer in the operation
2114 * @return string the piece of SQL code to be used in your statement.
2116 function sql_bitand($int1, $int2) {
2118 global $CFG;
2120 switch ($CFG->dbfamily) {
2121 case 'oracle':
2122 return 'bitand((' . $int1 . '), (' . $int2 . '))';
2123 break;
2124 default:
2125 return '((' . $int1 . ') & (' . $int2 . '))';
2130 * Returns the SQL text to be used in order to perform one bitwise OR operation
2131 * between 2 integers.
2132 * @param integer int1 first integer in the operation
2133 * @param integer int2 second integer in the operation
2134 * @return string the piece of SQL code to be used in your statement.
2136 function sql_bitor($int1, $int2) {
2138 global $CFG;
2140 switch ($CFG->dbfamily) {
2141 case 'oracle':
2142 return '((' . $int1 . ') + (' . $int2 . ') - ' . sql_bitand($int1, $int2) . ')';
2143 break;
2144 default:
2145 return '((' . $int1 . ') | (' . $int2 . '))';
2150 * Returns the SQL text to be used in order to perform one bitwise XOR operation
2151 * between 2 integers.
2152 * @param integer int1 first integer in the operation
2153 * @param integer int2 second integer in the operation
2154 * @return string the piece of SQL code to be used in your statement.
2156 function sql_bitxor($int1, $int2) {
2158 global $CFG;
2160 switch ($CFG->dbfamily) {
2161 case 'oracle':
2162 return '(' . sql_bitor($int1, $int2) . ' - ' . sql_bitand($int1, $int2) . ')';
2163 break;
2164 case 'postgres':
2165 return '((' . $int1 . ') # (' . $int2 . '))';
2166 break;
2167 default:
2168 return '((' . $int1 . ') ^ (' . $int2 . '))';
2173 * Returns the SQL text to be used in order to perform one bitwise NOT operation
2174 * with 1 integer.
2175 * @param integer int1 integer in the operation
2176 * @return string the piece of SQL code to be used in your statement.
2178 function sql_bitnot($int1) {
2180 global $CFG;
2182 switch ($CFG->dbfamily) {
2183 case 'oracle':
2184 return '((0 - (' . $int1 . ')) - 1)';
2185 break;
2186 default:
2187 return '(~(' . $int1 . '))';
2192 * Returns the FROM clause required by some DBs in all SELECT statements
2193 * To be used in queries not having FROM clause to provide cross_db
2195 function sql_null_from_clause() {
2196 global $CFG;
2198 switch ($CFG->dbfamily) {
2199 case 'oracle':
2200 return ' FROM dual';
2201 break;
2202 default:
2203 return '';
2208 * Returns the correct CEIL expression applied to fieldname
2209 * @param string fieldname the field (or expression) we are going to ceil
2210 * @return string the piece of SQL code to be used in your ceiling statement
2212 function sql_ceil($fieldname) {
2213 global $CFG;
2215 switch ($CFG->dbfamily) {
2216 case 'mssql':
2217 return ' CEILING(' . $fieldname . ')';
2218 break;
2219 default:
2220 return ' CEIL(' . $fieldname . ')';
2225 * Prepare a SQL WHERE clause to select records where the given fields match the given values.
2227 * Prepares a where clause of the form
2228 * WHERE field1 = value1 AND field2 = value2 AND field3 = value3
2229 * except that you need only specify as many arguments (zero to three) as you need.
2231 * @param string $field1 the first field to check (optional).
2232 * @param string $value1 the value field1 must have (requred if field1 is given, else optional).
2233 * @param string $field2 the second field to check (optional).
2234 * @param string $value2 the value field2 must have (requred if field2 is given, else optional).
2235 * @param string $field3 the third field to check (optional).
2236 * @param string $value3 the value field3 must have (requred if field3 is given, else optional).
2238 function where_clause($field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
2239 if ($field1) {
2240 $select = is_null($value1) ? "WHERE $field1 IS NULL" : "WHERE $field1 = '$value1'";
2241 if ($field2) {
2242 $select .= is_null($value2) ? " AND $field2 IS NULL" : " AND $field2 = '$value2'";
2243 if ($field3) {
2244 $select .= is_null($value3) ? " AND $field3 IS NULL" : " AND $field3 = '$value3'";
2247 } else {
2248 $select = '';
2250 return $select;
2254 * Get the data type of a table column, using an ADOdb MetaType() call.
2256 * @uses $CFG
2257 * @uses $db
2258 * @param string $table The name of the database table
2259 * @param string $column The name of the field in the table
2260 * @return string Field type or false if error
2263 function column_type($table, $column) {
2264 global $CFG, $db;
2266 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
2268 $sql = 'SELECT '.$column.' FROM '.$CFG->prefix.$table.' WHERE 1=2';
2269 if(!$rs = $db->Execute($sql)) {
2270 debugging($db->ErrorMsg() .'<br /><br />'. s($sql));
2271 if (!empty($CFG->dblogerror)) {
2272 $debug=array_shift(debug_backtrace());
2273 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $sql");
2275 return false;
2278 $field = $rs->FetchField(0);
2279 return $rs->MetaType($field->type);
2283 * This function will execute an array of SQL commands, returning
2284 * true/false if any error is found and stopping/continue as desired.
2285 * It's widely used by all the ddllib.php functions
2287 * @param array sqlarr array of sql statements to execute
2288 * @param boolean continue to specify if must continue on error (true) or stop (false)
2289 * @param boolean feedback to specify to show status info (true) or not (false)
2290 * @param boolean true if everything was ok, false if some error was found
2292 function execute_sql_arr($sqlarr, $continue=true, $feedback=true) {
2294 if (!is_array($sqlarr)) {
2295 return false;
2298 $status = true;
2299 foreach($sqlarr as $sql) {
2300 if (!execute_sql($sql, $feedback)) {
2301 $status = false;
2302 if (!$continue) {
2303 break;
2307 return $status;
2311 * This internal function, called from setup.php, sets all the configuration
2312 * needed to work properly against any DB. It setups connection encoding
2313 * and some other variables.
2315 * This function must contain the init code needed for each dbtype supported.
2317 function configure_dbconnection() {
2319 global $CFG, $db;
2321 switch ($CFG->dbtype) {
2322 case 'mysql':
2323 case 'mysqli':
2324 $db->Execute("SET NAMES 'utf8'");
2325 break;
2326 case 'postgres7':
2327 $db->Execute("SET NAMES 'utf8'");
2328 break;
2329 case 'mssql':
2330 case 'mssql_n':
2331 case 'odbc_mssql':
2332 /// No need to set charset. It must be specified in the driver conf
2333 /// Allow quoted identifiers
2334 $db->Execute('SET QUOTED_IDENTIFIER ON');
2335 /// Force ANSI nulls so the NULL check was done by IS NULL and NOT IS NULL
2336 /// instead of equal(=) and distinct(<>) simbols
2337 $db->Execute('SET ANSI_NULLS ON');
2338 /// Enable sybase quotes, so addslashes and stripslashes will use "'"
2339 ini_set('magic_quotes_sybase', '1');
2340 /// NOTE: Not 100% useful because GPC has been addslashed with the setting off
2341 /// so IT'S MANDATORY TO CHANGE THIS UNDER php.ini or .htaccess for this DB
2342 /// or to turn off magic_quotes to allow Moodle to do it properly
2343 break;
2344 case 'oci8po':
2345 /// No need to set charset. It must be specified by the NLS_LANG env. variable
2346 /// Enable sybase quotes, so addslashes and stripslashes will use "'"
2347 ini_set('magic_quotes_sybase', '1');
2348 /// NOTE: Not 100% useful because GPC has been addslashed with the setting off
2349 /// so IT'S MANDATORY TO ENABLE THIS UNDER php.ini or .htaccess for this DB
2350 /// or to turn off magic_quotes to allow Moodle to do it properly
2351 /// Now set the decimal separator to DOT, Moodle & PHP will always send floats to
2352 /// DB using DOTS. Manually introduced floats (if using other characters) must be
2353 /// converted back to DOTs (like gradebook does)
2354 $db->Execute("ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,'");
2355 break;
2360 * This function will handle all the records before being inserted/updated to DB for Oracle
2361 * installations. This is because the "special feature" of Oracle where the empty string is
2362 * equal to NULL and this presents a problem with all our currently NOT NULL default '' fields.
2364 * Once Moodle DB will be free of this sort of false NOT NULLS, this hack could be removed safely
2366 * Note that this function is 100% private and should be used, exclusively by DML functions
2367 * in this file. Also, this is considered a DIRTY HACK to be removed when possible. (stronk7)
2369 * This function is private and must not be used outside dmllib at all
2371 * @param $table string the table where the record is going to be inserted/updated (without prefix)
2372 * @param $dataobject object the object to be inserted/updated
2373 * @param $usecache boolean flag to determinate if we must use the per request cache of metadata
2374 * true to use it, false to ignore and delete it
2376 function oracle_dirty_hack ($table, &$dataobject, $usecache = true) {
2378 global $CFG, $db, $metadata_cache;
2380 /// Init and delete metadata cache
2381 if (!isset($metadata_cache) || !$usecache) {
2382 $metadata_cache = array();
2385 /// For Oracle DB, empty strings are converted to NULLs in DB
2386 /// and this breaks a lot of NOT NULL columns currenty Moodle. In the future it's
2387 /// planned to move some of them to NULL, if they must accept empty values and this
2388 /// piece of code will become less and less used. But, for now, we need it.
2389 /// What we are going to do is to examine all the data being inserted and if it's
2390 /// an empty string (NULL for Oracle) and the field is defined as NOT NULL, we'll modify
2391 /// such data in the best form possible ("0" for booleans and numbers and " " for the
2392 /// rest of strings. It isn't optimal, but the only way to do so.
2393 /// In the oppsite, when retrieving records from Oracle, we'll decode " " back to
2394 /// empty strings to allow everything to work properly. DIRTY HACK.
2396 /// If the db isn't Oracle, return without modif
2397 if ( $CFG->dbfamily != 'oracle') {
2398 return;
2401 /// Get Meta info to know what to change, using the cached meta if exists
2402 if (!isset($metadata_cache[$table])) {
2403 $metadata_cache[$table] = array_change_key_case($db->MetaColumns($CFG->prefix . $table), CASE_LOWER);
2405 $columns = $metadata_cache[$table];
2406 /// Iterate over all the fields in the insert, transforming values
2407 /// in the best possible form
2408 foreach ($dataobject as $fieldname => $fieldvalue) {
2409 /// If the field doesn't exist in metadata, skip
2410 if (!isset($columns[strtolower($fieldname)])) {
2411 continue;
2413 /// If the field ins't VARCHAR or CLOB, skip
2414 if ($columns[strtolower($fieldname)]->type != 'VARCHAR2' && $columns[strtolower($fieldname)]->type != 'CLOB') {
2415 continue;
2417 /// If the field isn't NOT NULL, skip (it's nullable, so accept empty values)
2418 if (!$columns[strtolower($fieldname)]->not_null) {
2419 continue;
2421 /// If the value isn't empty, skip
2422 if (!empty($fieldvalue)) {
2423 continue;
2425 /// Now, we have one empty value, going to be inserted to one NOT NULL, VARCHAR2 or CLOB field
2426 /// Try to get the best value to be inserted
2428 /// The '0' string doesn't need any transformation, skip
2429 if ($fieldvalue === '0') {
2430 continue;
2433 /// Transformations start
2434 if (gettype($fieldvalue) == 'boolean') {
2435 $dataobject->$fieldname = '0'; /// Transform false to '0' that evaluates the same for PHP
2436 } else if (gettype($fieldvalue) == 'integer') {
2437 $dataobject->$fieldname = '0'; /// Transform 0 to '0' that evaluates the same for PHP
2438 } else if (gettype($fieldvalue) == 'NULL') {
2439 $dataobject->$fieldname = '0'; /// Transform NULL to '0' that evaluates the same for PHP
2440 } else if ($fieldvalue === '') {
2441 $dataobject->$fieldname = ' '; /// Transform '' to ' ' that DONT'T EVALUATE THE SAME
2442 /// (we'll transform back again on get_records_XXX functions and others)!!
2446 /// End of DIRTY HACK
2449 * This function will search for all the CLOBs and BLOBs fields passed in the dataobject, replacing
2450 * their contents by the fixed strings '@#CLOB#@' and '@#BLOB#@' and returning one array for all the
2451 * found CLOBS and another for all the found BLOBS
2452 * Used by Oracle drivers to perform the two-step insertion/update of LOBs and
2453 * by MSSQL to perform the same exclusively for BLOBs (IMAGE fields)
2455 * This function is private and must not be used outside dmllib at all
2457 * @param $table string the table where the record is going to be inserted/updated (without prefix)
2458 * @param $dataobject object the object to be inserted/updated
2459 * @param $clobs array of clobs detected
2460 * @param $dataobject array of blobs detected
2461 * @param $unset boolean to specify if we must unset found LOBs from the original object (true) or
2462 * just return them modified to @#CLOB#@ and @#BLOB#@ (false)
2463 * @param $usecache boolean flag to determinate if we must use the per request cache of metadata
2464 * true to use it, false to ignore and delete it
2466 function db_detect_lobs ($table, &$dataobject, &$clobs, &$blobs, $unset = false, $usecache = true) {
2468 global $CFG, $db, $metadata_cache;
2470 $dataarray = (array)$dataobject; //Convert to array. It's supposed that PHP 4.3 doesn't iterate over objects
2472 /// Initial configuration, based on DB
2473 switch ($CFG->dbfamily) {
2474 case 'oracle':
2475 $clobdbtype = 'CLOB'; //Name of clobs for this DB
2476 $blobdbtype = 'BLOB'; //Name of blobs for this DB
2477 break;
2478 case 'mssql':
2479 $clobdbtype = 'NOTPROCESSES'; //Name of clobs for this DB (under mssql flavours we don't process CLOBS)
2480 $blobdbtype = 'IMAGE'; //Name of blobs for this DB
2481 break;
2482 case 'postgres':
2483 $clobdbtype = 'NOTPROCESSES'; //Name of clobs for this DB (under postgres flavours we don't process CLOBS)
2484 $blobdbtype = 'BYTEA'; //Name of blobs for this DB
2485 break;
2486 default:
2487 return; //Other DB doesn't need this two step to happen, prevent continue
2490 /// Init and delete metadata cache
2491 if (!isset($metadata_cache) || !$usecache) {
2492 $metadata_cache = array();
2495 /// Get Meta info to know what to change, using the cached meta if exists
2496 if (!isset($metadata_cache[$table])) {
2497 $metadata_cache[$table] = array_change_key_case($db->MetaColumns($CFG->prefix . $table), CASE_LOWER);
2499 $columns = $metadata_cache[$table];
2501 foreach ($dataarray as $fieldname => $fieldvalue) {
2502 /// If the field doesn't exist in metadata, skip
2503 if (!isset($columns[strtolower($fieldname)])) {
2504 continue;
2506 /// If the field is CLOB, update its value to '@#CLOB#@' and store it in the $clobs array
2507 if (strtoupper($columns[strtolower($fieldname)]->type) == $clobdbtype) {
2508 /// Oracle optimization. CLOBs under 4000cc can be directly inserted (no need to apply 2-phases to them)
2509 if ($CFG->dbfamily == 'oracle' && strlen($dataobject->$fieldname) < 4000) {
2510 continue;
2512 $clobs[$fieldname] = $dataobject->$fieldname;
2513 if ($unset) {
2514 unset($dataobject->$fieldname);
2515 } else {
2516 $dataobject->$fieldname = '@#CLOB#@';
2518 continue;
2521 /// If the field is BLOB OR IMAGE OR BYTEA, update its value to '@#BLOB#@' and store it in the $blobs array
2522 if (strtoupper($columns[strtolower($fieldname)]->type) == $blobdbtype) {
2523 $blobs[$fieldname] = $dataobject->$fieldname;
2524 if ($unset) {
2525 unset($dataobject->$fieldname);
2526 } else {
2527 $dataobject->$fieldname = '@#BLOB#@';
2529 continue;
2535 * This function will iterate over $clobs and $blobs array, executing the needed
2536 * UpdateClob() and UpdateBlob() ADOdb function calls to store LOBs contents properly
2537 * Records to be updated are always searched by PK (id always!)
2539 * Used by Orace CLOBS and BLOBS and MSSQL IMAGES
2541 * This function is private and must not be used outside dmllib at all
2543 * @param $table string the table where the record is going to be inserted/updated (without prefix)
2544 * @param $sqlcondition mixed value defining the records to be LOB-updated. It it's a number, must point
2545 * to the PK og the table (id field), else it's processed as one harcoded SQL condition (WHERE clause)
2546 * @param $clobs array of clobs to be updated
2547 * @param $blobs array of blobs to be updated
2549 function db_update_lobs ($table, $sqlcondition, &$clobs, &$blobs) {
2551 global $CFG, $db;
2553 $status = true;
2555 /// Initial configuration, based on DB
2556 switch ($CFG->dbfamily) {
2557 case 'oracle':
2558 $clobdbtype = 'CLOB'; //Name of clobs for this DB
2559 $blobdbtype = 'BLOB'; //Name of blobs for this DB
2560 break;
2561 case 'mssql':
2562 $clobdbtype = 'NOTPROCESSES'; //Name of clobs for this DB (under mssql flavours we don't process CLOBS)
2563 $blobdbtype = 'IMAGE'; //Name of blobs for this DB
2564 break;
2565 case 'postgres':
2566 $clobdbtype = 'NOTPROCESSES'; //Name of clobs for this DB (under postgres flavours we don't process CLOBS)
2567 $blobdbtype = 'BYTEA'; //Name of blobs for this DB
2568 break;
2569 default:
2570 return; //Other DB doesn't need this two step to happen, prevent continue
2573 /// Calculate the update sql condition
2574 if (is_numeric($sqlcondition)) { /// If passing a number, it's the PK of the table (id)
2575 $sqlcondition = 'id=' . $sqlcondition;
2576 } else { /// Else, it's a formal standard SQL condition, we try to delete the WHERE in case it exists
2577 $sqlcondition = trim(preg_replace('/^WHERE/is', '', trim($sqlcondition)));
2580 /// Update all the clobs
2581 if ($clobs) {
2582 foreach ($clobs as $key => $value) {
2584 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; }; /// Count the extra updates in PERF
2586 /// Oracle CLOBs doesn't like quoted strings (are inserted via prepared statemets)
2587 if ($CFG->dbfamily == 'oracle') {
2588 $value = stripslashes_safe($value);
2591 if (!$db->UpdateClob($CFG->prefix.$table, $key, $value, $sqlcondition)) {
2592 $status = false;
2593 $statement = "UpdateClob('$CFG->prefix$table', '$key', '" . substr($value, 0, 100) . "...', '$sqlcondition')";
2594 debugging($db->ErrorMsg() ."<br /><br />".s($statement));
2595 if (!empty($CFG->dblogerror)) {
2596 $debug=array_shift(debug_backtrace());
2597 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $statement");
2602 /// Update all the blobs
2603 if ($blobs) {
2604 foreach ($blobs as $key => $value) {
2606 if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; }; /// Count the extra updates in PERF
2608 /// Oracle, MSSQL and PostgreSQL BLOBs doesn't like quoted strings (are inserted via prepared statemets)
2609 if ($CFG->dbfamily == 'oracle' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'postgres') {
2610 $value = stripslashes_safe($value);
2613 if(!$db->UpdateBlob($CFG->prefix.$table, $key, $value, $sqlcondition)) {
2614 $status = false;
2615 $statement = "UpdateBlob('$CFG->prefix$table', '$key', '" . substr($value, 0, 100) . "...', '$sqlcondition')";
2616 debugging($db->ErrorMsg() ."<br /><br />".s($statement));
2617 if (!empty($CFG->dblogerror)) {
2618 $debug=array_shift(debug_backtrace());
2619 error_log("SQL ".$db->ErrorMsg()." in {$debug['file']} on line {$debug['line']}. STATEMENT: $statement");
2624 return $status;
2628 * Set cached record.
2630 * If you have called rcache_getforfill() before, it will also
2631 * release the lock.
2633 * This function is private and must not be used outside dmllib at all
2635 * @param $table string
2636 * @param $id integer
2637 * @param $rec obj
2638 * @return bool
2640 function rcache_set($table, $id, $rec) {
2641 global $CFG, $MCACHE, $rcache;
2643 if ($CFG->cachetype === 'internal') {
2644 if (!isset($rcache->data[$table])) {
2645 $rcache->data[$table] = array();
2647 if (!isset($rcache->data[$table][$id]) and count($rcache->data[$table]) > $CFG->intcachemax) {
2648 // release oldes record
2649 reset($rcache->data[$table]);
2650 $key = key($rcache->data[$table]);
2651 unset($rcache->data[$table][$key]);
2653 $rcache->data[$table][$id] = clone($rec);
2654 } else {
2655 $key = $table . '|' . $id;
2657 if (isset($MCACHE)) {
2658 // $table is a flag used to mark
2659 // a table as dirty & uncacheable
2660 // when an UPDATE or DELETE not bound by ID
2661 // is taking place
2662 if (!$MCACHE->get($table)) {
2663 // this will also release the _forfill lock
2664 $MCACHE->set($key, $rec, $CFG->rcachettl);
2668 return true;
2673 * Unset cached record if it exists.
2675 * This function is private and must not be used outside dmllib at all
2677 * @param $table string
2678 * @param $id integer
2679 * @return bool
2681 function rcache_unset($table, $id) {
2682 global $CFG, $MCACHE, $rcache;
2684 if ($CFG->cachetype === 'internal') {
2685 if (isset($rcache->data[$table][$id])) {
2686 unset($rcache->data[$table][$id]);
2688 } else {
2689 $key = $table . '|' . $id;
2690 if (isset($MCACHE)) {
2691 $MCACHE->delete($key);
2694 return true;
2698 * Get cached record if available. ONLY use if you
2699 * are trying to get the cached record and will NOT
2700 * fetch it yourself if not cached.
2702 * Use rcache_getforfill() if you are going to fetch
2703 * the record if not cached...
2705 * This function is private and must not be used outside dmllib at all
2707 * @param $table string
2708 * @param $id integer
2709 * @return mixed object-like record on cache hit, false otherwise
2711 function rcache_get($table, $id) {
2712 global $CFG, $MCACHE, $rcache;
2714 if ($CFG->cachetype === 'internal') {
2715 if (isset($rcache->data[$table][$id])) {
2716 $rcache->hits++;
2717 return clone($rcache->data[$table][$id]);
2718 } else {
2719 $rcache->misses++;
2720 return false;
2724 if (isset($MCACHE)) {
2725 $key = $table . '|' . $id;
2726 // we set $table as a flag used to mark
2727 // a table as dirty & uncacheable
2728 // when an UPDATE or DELETE not bound by ID
2729 // is taking place
2730 if ($MCACHE->get($table)) {
2731 $rcache->misses++;
2732 return false;
2733 } else {
2734 $rec = $MCACHE->get($key);
2735 if (!empty($rec)) {
2736 $rcache->hits++;
2737 return $rec;
2738 } else {
2739 $rcache->misses++;
2740 return false;
2744 return false;
2748 * Get cached record if available. In most cases you want
2749 * to use this function -- namely if you are trying to get
2750 * the cached record and will fetch it yourself if not cached.
2751 * (and set the cache ;-)
2753 * Uses the getforfill caching mechanism. See lib/eaccelerator.class.php
2754 * for a detailed description of the technique.
2756 * Note: if you call rcache_getforfill() you are making an implicit promise
2757 * that if the cache is empty, you will later populate it, or cancel the promise
2758 * calling rcache_releaseforfill();
2760 * This function is private and must not be used outside dmllib at all
2762 * @param $table string
2763 * @param $id integer
2764 * @return mixed object-like record on cache hit, false otherwise
2766 function rcache_getforfill($table, $id) {
2767 global $CFG, $MCACHE, $rcache;
2769 if ($CFG->cachetype === 'internal') {
2770 return rcache_get($table, $id);
2773 if (isset($MCACHE)) {
2774 $key = $table . '|' . $id;
2775 // if $table is set - we won't take the
2776 // lock either
2777 if ($MCACHE->get($table)) {
2778 $rcache->misses++;
2779 return false;
2781 $rec = $MCACHE->getforfill($key);
2782 if (!empty($rec)) {
2783 $rcache->hits++;
2784 return $rec;
2786 $rcache->misses++;
2787 return false;
2789 return false;
2793 * Release the exclusive lock obtained by
2794 * rcache_getforfill(). See rcache_getforfill()
2795 * for more details.
2797 * This function is private and must not be used outside dmllib at all
2799 * @param $table string
2800 * @param $id integer
2801 * @return bool
2803 function rcache_releaseforfill($table, $id) {
2804 global $CFG, $MCACHE;
2806 if (isset($MCACHE)) {
2807 $key = $table . '|' . $id;
2808 return $MCACHE->releaseforfill($key);
2810 return true;
2814 * Remove or invalidate all rcache entries related to
2815 * a table. Not all caching mechanisms cluster entries
2816 * by table so in those cases we use alternative strategies.
2818 * This function is private and must not be used outside dmllib at all
2820 * @param $table string the table to invalidate records for
2821 * @return bool
2823 function rcache_unset_table ($table) {
2824 global $CFG, $MCACHE, $rcache;
2826 if ($CFG->cachetype === 'internal') {
2827 if (isset($rcache->data[$table])) {
2828 unset($rcache->data[$table]);
2830 return true;
2833 if (isset($MCACHE)) {
2834 // at least as long as content keys to ensure they expire
2835 // before the dirty flag
2836 $MCACHE->set($table, true, $CFG->rcachettl);
2838 return true;