aboutsummaryrefslogtreecommitdiff
path: root/engine
diff options
context:
space:
mode:
Diffstat (limited to 'engine')
-rw-r--r--engine/lib/database.php330
1 files changed, 248 insertions, 82 deletions
diff --git a/engine/lib/database.php b/engine/lib/database.php
index 798f3ef59..d39fb1151 100644
--- a/engine/lib/database.php
+++ b/engine/lib/database.php
@@ -1,32 +1,83 @@
<?php
/**
- * Elgg database
- * Contains database connection and transfer functionality
+ * Elgg database procedural code.
+ *
+ * Includes functions for establishing and retrieving a database link,
+ * reading data, writing data, upgrading DB schemas, and sanitizing input.
*
* @package Elgg
* @subpackage Core
-
- * @author Curverider Ltd
-
- * @link http://elgg.org/
*/
+/**
+ * Query cache for all queries.
+ *
+ * Each query and its results are stored in this array as:
+ * <code>
+ * $DB_QUERY_CACHE[$query] => array(result1, result2, ... resultN)
+ * </code>
+ *
+ * @global array $DB_QUERY_CACHE
+ */
$DB_QUERY_CACHE = array();
+
+/**
+ * Queries to be executed upon shutdown.
+ *
+ * These queries are saved to an array and executed using
+ * a function registered by register_shutdown_function().
+ *
+ * Queries are saved as an array in the format:
+ * <code>
+ * $DB_DELAYED_QUERIES[] = array(
+ * 'q' => str $query,
+ * 'l' => resource $dblink,
+ * 'h' => str $handler // a callback function
+ * );
+ * </code>
+ *
+ * @see db_delayedexecution_shutdown_hook()
+ * @see execute_delayed_query()
+ * @see init_db()
+ * @see register_shutdown_function()
+ * @global array $DB_DELAYED_QUERIES
+ */
$DB_DELAYED_QUERIES = array();
/**
+ * Database connection resources.
+ *
+ * Each database link created with establish_db_link($name) is stored in
+ * $dblink as $dblink[$name] => resource. Use get_db_link($name) to retrieve it.
+ *
+ * @global array $dblink
+ */
+$dblink = array();
+
+/**
+ * Database call count
+ *
+ * Each call to the database increments this counter.
+ *
+ * @global integer $dbcalls
+ */
+$dbcalls = 0;
+
+/**
+ * Establish a connection to the database servser
+ *
* Connect to the database server and use the Elgg database for a particular database link
*
- * @param string $dblinkname Default "readwrite"; you can change this to set up additional global database links, eg "read" and "write"
+ * @param string $dblinkname The type of database connection. Used to identify the resource. eg "read", "write", or "readwrite".
+ * @uses $CONFIG
+ * @uses $dblink
+ * @uses $DB_QUERY_CACHE
+ * @uses $dbcalls;
*/
function establish_db_link($dblinkname = "readwrite") {
// Get configuration, and globalise database link
global $CONFIG, $dblink, $DB_QUERY_CACHE, $dbcalls;
- if (!isset($dblink)) {
- $dblink = array();
- }
-
if ($dblinkname != "readwrite" && isset($CONFIG->db[$dblinkname])) {
if (is_array($CONFIG->db[$dblinkname])) {
$index = rand(0,sizeof($CONFIG->db[$dblinkname]));
@@ -53,6 +104,7 @@ function establish_db_link($dblinkname = "readwrite") {
$CONFIG->dbuser, $CONFIG->dbhost, "****");
throw new DatabaseException($msg);
}
+
if (!mysql_select_db($CONFIG->dbname, $dblink[$dblinkname])) {
$msg = sprintf(elgg_echo('DatabaseException:NoConnect'), $CONFIG->dbname);
throw new DatabaseException($msg);
@@ -68,20 +120,21 @@ function establish_db_link($dblinkname = "readwrite") {
// Set up cache if global not initialized and query cache not turned off
if ((!$DB_QUERY_CACHE) && (!$db_cache_off)) {
- $DB_QUERY_CACHE = new ElggStaticVariableCache('db_query_cache'); //array();
- //$DB_QUERY_CACHE = select_default_memcache('db_query_cache'); //array();
+ $DB_QUERY_CACHE = new ElggStaticVariableCache('db_query_cache');
}
}
/**
- * Establish all database connections
+ * Establish database connections
*
* If the configuration has been set up for multiple read/write databases, set those
- * links up separately; otherwise just create the one database link
+ * links up separately; otherwise just create the one database link.
*
+ * @uses $CONFIG
+ * @uses $dblink
+ * @uses establish_db_link
*/
function setup_db_connections() {
- // Get configuration and globalise database link
global $CONFIG, $dblink;
if (!empty($CONFIG->db->split)) {
@@ -93,7 +146,11 @@ function setup_db_connections() {
}
/**
- * Shutdown hook to display profiling information about db (debug mode)
+ * Display profiling information about db at NOTICE debug level upon shutdown.
+ *
+ * @uses $dbcalls
+ * @uses init_db()
+ * @uses register_shutdown_function()
*/
function db_profiling_shutdown_hook() {
global $dbcalls;
@@ -103,7 +160,12 @@ function db_profiling_shutdown_hook() {
}
/**
- * Execute any delayed queries.
+ * Execute any delayed queries upon shutdown.
+ *
+ * @uses $DB_DELAYED_QUERIES
+ * @uses $CONFIG
+ * @uses init_db()
+ * @uses register_shutdown_function()
*/
function db_delayedexecution_shutdown_hook() {
global $DB_DELAYED_QUERIES, $CONFIG;
@@ -116,31 +178,42 @@ function db_delayedexecution_shutdown_hook() {
if ((isset($query_details['h'])) && (is_callable($query_details['h']))) {
$query_details['h']($result);
}
- } catch (Exception $e) { // Suppress all errors since these can't be delt with here
+ } catch (Exception $e) {
+ // Suppress all errors since these can't be dealt with here
elgg_log($e, 'WARNING');
}
}
}
/**
- * Alias to setup_db_connections, for use in the event handler
+ * Registers shutdown functions for database profiling and delayed queries.
+ *
+ * NB: Database connections are established upon first call to database.
*
* @param string $event The event type
* @param string $object_type The object type
* @param mixed $object Used for nothing in this context
+ * @elgg_event_handler boot system
*/
-function init_db($event, $object_type, $object = null) {
+function init_db() {
register_shutdown_function('db_delayedexecution_shutdown_hook');
register_shutdown_function('db_profiling_shutdown_hook');
- // [Marcus Povey 20090213: Db connection moved to first db connection attempt]
+
return true;
}
/**
- * Gets the appropriate db link for the operation mode requested
+ * Returns (if required, also creates) a database link resource.
+ *
+ * Database link resources are stored in the {@link $dblink} global. These
+ * resources are created by {@link setup_db_connections()}, which is called if
+ * no links exist.
*
- * @param string $dblinktype The type of link we want - "read", "write" or "readwrite" (the default)
+ * @param string $dblinktype The type of link we want: "read", "write" or "readwrite".
* @return object Database link
+ * @uses setup_db_connections()
+ * @uses get_db_link()
+ * @uses $dblink
*/
function get_db_link($dblinktype) {
global $dblink;
@@ -149,30 +222,42 @@ function get_db_link($dblinktype) {
return $dblink[$dblinktype];
} else if (isset($dblink['readwrite'])) {
return $dblink['readwrite'];
- }
- else {
+ } else {
setup_db_connections();
return get_db_link($dblinktype);
}
}
/**
- * Explain a given query, useful for debug.
+ * Execute an EXPLAIN for $query.
+ *
+ * @param str $query The query to explain
+ * @param mixed $link The database link resource to user.
+ * @return mixed An object of the query's result, or FALSE
+ * @uses execute_query()
*/
function explain_query($query, $link) {
if ($result = execute_query("explain " . $query, $link)) {
return mysql_fetch_object($result);
}
- return false;
+ return FALSE;
}
/**
* Execute a query.
*
+ * {@link $dbcalls} is incremented and $query is executed via {@link mysql_query()}.
+ * The query is saved into the {@link $DB_QUERY_CACHE}. If there is an SQL error,
+ * a {@link DatabaseException} is thrown.
+ *
* @param string $query The query
* @param link $dblink the DB link
- * @return Returns a the result of mysql_query
+ * @return The result of mysql_query()
+ * @uses $CONFIG
+ * @uses $dbcalls
+ * @uses $DB_QUERY_CACHE
+ * @throws DatabaseException
*/
function execute_query($query, $dblink) {
global $CONFIG, $dbcalls, $DB_QUERY_CACHE;
@@ -192,14 +277,14 @@ function execute_query($query, $dblink) {
}
/**
- * Queue a query for execution after all output has been sent to the user.
+ * Queue a query for execution upon shutdown.
*
* You can specify a handler function if you care about the result. This function will accept
- * the raw result from mysql_query();
+ * the raw result from {@link mysql_query()}.
*
* @param string $query The query to execute
* @param resource $dblink The database link to use
- * @param string $handler The handler
+ * @param string $handler A callback function to pass the results array to
*/
function execute_delayed_query($query, $dblink, $handler = "") {
global $DB_DELAYED_QUERIES;
@@ -216,7 +301,7 @@ function execute_delayed_query($query, $dblink, $handler = "") {
$DB_DELAYED_QUERIES[] = $delayed_query;
- return true;
+ return TRUE;
}
/**
@@ -224,6 +309,8 @@ function execute_delayed_query($query, $dblink, $handler = "") {
*
* @param string $query The query to execute
* @param string $handler The handler if you care about the result.
+ * @uses execute_delayed_query()
+ * @uses get_db_link()
*/
function execute_delayed_write_query($query, $handler = "") {
return execute_delayed_query($query, get_db_link('write'), $handler);
@@ -234,16 +321,31 @@ function execute_delayed_write_query($query, $handler = "") {
*
* @param string $query The query to execute
* @param string $handler The handler if you care about the result.
+ * @uses execute_delayed_query()
+ * @uses get_db_link()
*/
function execute_delayed_read_query($query, $handler = "") {
return execute_delayed_query($query, get_db_link('read'), $handler);
}
/**
- * Use this function to get data from the database
+ * Retrieve rows from the database.
+ *
+ * Queries are executed with {@link execute_query()} and results
+ * are retrieved with {@link mysql_fetch_object()}. If a callback
+ * function $callback is defined, each row will be passed as the single
+ * argument to $callback. If no callback function is defined, the
+ * entire result set is returned as an array.
+ *
+ * If no results are matched, FALSE is returned.
+ *
* @param mixed $query The query being passed.
- * @param string $call Optionally, the name of a function to call back to on each row (which takes $row as a single parameter)
- * @return array An array of database result objects
+ * @param string $call Optionally, the name of a function to call back to on each row
+ * @return array|false An array of database result objects or callback function results or false
+ * @uses $CONFIG
+ * @uses $DB_QUERY_CACHE
+ * @uses get_db_link()
+ * @uses execute_query()
*/
function get_data($query, $callback = "") {
global $CONFIG, $DB_QUERY_CACHE;
@@ -281,7 +383,7 @@ function get_data($query, $callback = "") {
if (empty($resultarray)) {
elgg_log("DB query \"$query\" returned no results.");
// @todo consider changing this to return empty array #1242
- return false;
+ return FALSE;
}
// Cache result
@@ -294,11 +396,19 @@ function get_data($query, $callback = "") {
}
/**
- * Use this function to get a single data row from the database
- * @param mixed $query The query to run.
- * @return object A single database result object
+ * Retrieve a single row from the database.
+ *
+ * Similar to {@link get_data()} but returns only the first row
+ * matched. If a callback function $callback is specified, the row will be passed
+ * as the only argument to $callback.
+ *
+ * @param mixed $query The query to execute.
+ * @return mixed A single database result object or the result of the callback function.
+ * @uses $CONFIG
+ * @uses $DB_QUERY_CACHE
+ * @uses execute_query()
+ * @uses get_db_link()
*/
-
function get_data_row($query, $callback = "") {
global $CONFIG, $DB_QUERY_CACHE;
@@ -313,7 +423,7 @@ function get_data_row($query, $callback = "") {
if ($cached_query === -1) {
// Last time this query returned nothing, so return false
//@todo fix me this should return array().
- return false;
+ return FALSE;
}
return $cached_query;
@@ -344,10 +454,17 @@ function get_data_row($query, $callback = "") {
}
/**
- * Use this function to insert database data; returns id or false
+ * Insert a row into the database.
+ *
+ * NB: Altering the DB invalidates all queries in {@link $DB_QUERY_CACHE}.
*
- * @param mixed $query The query to run.
- * @return int $id the database id of the inserted row.
+ * @param mixed $query The query to execute.
+ * @return int|false The database id of the inserted row if a AUTO_INCREMENT field is defined, 0 if not, and false on failure.
+ * @uses $CONFIG
+ * @uses $DB_QUERY_CACHE
+ * @uses get_db_link()
+ * @uses execute_query()
+ * @uses mysql_insert_id()
*/
function insert_data($query) {
global $CONFIG, $DB_QUERY_CACHE;
@@ -365,14 +482,20 @@ function insert_data($query) {
return mysql_insert_id($dblink);
}
- return false;
+ return FALSE;
}
/**
- * Update database data
+ * Update a row in the database.
+ *
+ * NB: Altering the DB invalidates all queries in {@link $DB_QUERY_CACHE}.
*
- * @param mixed $query The query to run.
- * @return Bool on success
+ * @param string $query The query to run.
+ * @return Bool
+ * @uses $CONFIG
+ * @uses $DB_QUERY_CACHE
+ * @uses get_db_link()
+ * @uses execute_db_query()
*/
function update_data($query) {
global $CONFIG, $DB_QUERY_CACHE;
@@ -386,19 +509,23 @@ function update_data($query) {
}
if (execute_query("$query", $dblink)) {
- // @todo why is this comment out?
- //return mysql_affected_rows();
- return true;
+ return TRUE;
}
- return false;
+ return FALSE;
}
/**
- * Use this function to delete data
+ * Remove a row from the database.
*
- * @param mixed $query The SQL query to run
- * @return int|false Either the number of affected rows, or false on failure
+ * NB: Altering the DB invalidates all queries in {@link $DB_QUERY_CACHE}.
+ *
+ * @param string $query The SQL query to run
+ * @return int|false The number of affected rows or false on failure
+ * @uses $CONFIG
+ * @uses $DB_QUERY_CACHE
+ * @uses execute_query()
+ * @uses mysql_affected_rows()
*/
function delete_data($query) {
global $CONFIG, $DB_QUERY_CACHE;
@@ -415,14 +542,17 @@ function delete_data($query) {
return mysql_affected_rows($dblink);
}
- return false;
+ return FALSE;
}
/**
- * Get the tables currently installed in the Elgg database
+ * Return tables matching the database prefix {@link $CONFIG->dbprefix}% in the currently
+ * selected database.
*
- * @return array List of tables
+ * @return array|false List of tables or false on failure
+ * @uses $CONFIG
+ * @static array $tables Tables found matching the database prefix
*/
function get_db_tables() {
global $CONFIG;
@@ -436,7 +566,7 @@ function get_db_tables() {
$result = get_data("show tables like '" . $CONFIG->dbprefix . "%'");
} catch (DatabaseException $d) {
// Likely we can't handle an exception here, so just return false.
- return false;
+ return FALSE;
}
$tables = array();
@@ -450,15 +580,19 @@ function get_db_tables() {
}
}
} else {
- return false;
+ return FALSE;
}
return $tables;
}
/**
- * Run an optimize query on a mysql tables. Useful for executing after major data changes.
+ * Optimise a table.
+ *
+ * Executes an OPTIMIZE TABLE query on $table. Useful after large DB changes.
*
+ * @param string $table The name of the table to optimise
+ * @uses update_data()
*/
function optimize_table($table) {
$table = sanitise_string($table);
@@ -468,7 +602,7 @@ function optimize_table($table) {
/**
* Get the last database error for a particular database link
*
- * @param database link $dblink
+ * @param resource $dblink
* @return string Database error message
*/
function get_db_error($dblink) {
@@ -476,10 +610,24 @@ function get_db_error($dblink) {
}
/**
- * Runs a full database script from disk
+ * Runs a full database script from disk.
+ *
+ * The file specified should be a standard SQL file as created by
+ * mysqldump or similar. Statements must be terminated with ;
+ * and a newline character (\n or \r\n) with only one statement per line.
+ *
+ * The special string 'prefix_' is replaced with the database prefix
+ * as defined in {@link $CONFIG->dbprefix}.
+ *
+ * NB: Errors do not halt execution of the script. If a line
+ * generates an error, the error message is saved and the
+ * next line is executed. After the file is run, any errors
+ * are displayed as a {@link DatabaseException}
*
- * @uses $CONFIG
* @param string $scriptlocation The full path to the script
+ * @throws DatabaseException
+ * @uses $CONFIG
+ * @uses update_data()
*/
function run_sql_script($scriptlocation) {
if ($script = file_get_contents($scriptlocation)) {
@@ -487,8 +635,12 @@ function run_sql_script($scriptlocation) {
$errors = array();
+ // Remove MySQL -- style comments
$script = preg_replace('/\-\-.*\n/', '', $script);
+
+ // Statements must end with ; and a newline
$sql_statements = preg_split('/;[\n\r]+/', $script);
+
foreach($sql_statements as $statement) {
$statement = trim($statement);
$statement = str_replace("prefix_",$CONFIG->dbprefix,$statement);
@@ -502,8 +654,9 @@ function run_sql_script($scriptlocation) {
}
if (!empty($errors)) {
$errortxt = "";
- foreach($errors as $error)
+ foreach($errors as $error) {
$errortxt .= " {$error};";
+ }
throw new DatabaseException(elgg_echo('DatabaseException:DBSetupIssues') . $errortxt);
}
} else {
@@ -514,14 +667,22 @@ function run_sql_script($scriptlocation) {
/**
* Upgrade the database schema in an ordered sequence.
*
- * Makes use of schema upgrade files
+ * Executes all upgrade files in elgg/engine/schema/upgrades/ in sequential order.
+ * Upgrade files must be in the standard Elgg release format of YYYYMMDDII.sql
+ * where II is an incrementor starting from 01.
+ *
+ * Files that are < $version will be ignored.
*
- * This is a about as core as it comes, so don't start running this from your plugins!
+ * NB: Plugin authors should not call this function directly.
*
- * @param int $version The version you are upgrading from (usually given in the Elgg version format of YYYYMMDDXX - see version.php for example)
+ * @param int $version The version you are upgrading from in the format YYYYMMDDII.
* @param string $fromdir Optional directory to load upgrades from (default: engine/schema/upgrades/)
- * @param bool $quiet If true, will suppress all error messages. Don't use this.
+ * @param bool $quiet If true, will suppress all error messages. Should be used only for the upgrade from version <=1.6.
* @return bool
+ * @uses $CONFIG
+ * @uses run_sql_script()
+ * @see upgrade.php
+ * @see version.php
*/
function db_upgrade($version, $fromdir = "", $quiet = FALSE) {
global $CONFIG;
@@ -574,27 +735,31 @@ function db_upgrade($version, $fromdir = "", $quiet = FALSE) {
}
/**
- * This function, called by validate_platform(), will check whether the installed version of
- * MySQL meets the minimum required.
+ * Check if MySQL is at least version 5.
*
* @todo If multiple dbs are supported check which db is supported and use the appropriate code to validate
* the appropriate version.
- *
* @return bool
+ * @see mysql_get_server_info()
*/
function db_check_version() {
$version = mysql_get_server_info();
$points = explode('.', $version);
if ($points[0] < 5) {
- return false;
+ return FALSE;
}
- return true;
+ return TRUE;
}
/**
* Sanitise a string for database use, but with the option of escaping extra characters.
+ *
+ * @param string $string The string to sanitise
+ * @param string $extra_escapeable Extra characters to escape with '\\'
+ * @return string The escaped string
+ * @uses sanitise_string()
*/
function sanitise_string_special($string, $extra_escapeable = '') {
$string = sanitise_string($string);
@@ -607,7 +772,7 @@ function sanitise_string_special($string, $extra_escapeable = '') {
}
/**
- * Sanitise a string for database use
+ * Sanitise a string for database use.
*
* @param string $string The string to sanitise
* @return string Sanitised string
@@ -623,14 +788,14 @@ function sanitise_string($string) {
*
* @param string $string The string to sanitise
* @return string Sanitised string
- * @uses sanitise_string
+ * @uses sanitise_string()
*/
function sanitize_string($string) {
return sanitise_string($string);
}
/**
- * Sanitises an integer for database use
+ * Sanitises an integer for database use.
*
* @param int $int
* @return int Sanitised integer
@@ -644,12 +809,13 @@ function sanitise_int($int) {
*
* @param int $int
* @return int Sanitised integer
- * @uses sanitise_string
+ * @uses sanitise_int()
*/
function sanitize_int($int) {
return (int) $int;
}
-// Stuff for initialisation
-
-register_elgg_event_handler('boot','system','init_db',0); \ No newline at end of file
+/**
+ * @elgg_register_event boot system init_db
+ */
+register_elgg_event_handler('boot', 'system', 'init_db', 0); \ No newline at end of file