X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2Fdba.php;h=3af8522516c64ca9f4306cdff9b836f02302bfac;hb=d7b420a44e9e196a51b78b60bc78a491757cf299;hp=11e1d2131d27f74a9d5c0ebf5057c79e0858a3e8;hpb=b9b43e30e6a7222cdf29880a4600cb56be38250f;p=friendica.git diff --git a/include/dba.php b/include/dba.php index 11e1d2131d..3af8522516 100644 --- a/include/dba.php +++ b/include/dba.php @@ -22,14 +22,24 @@ class dba { public $connected = false; public $error = false; private $_server_info = ''; + private static $in_transaction = false; private static $dbo; private static $relation = array(); - function __construct($server, $user, $pass, $db, $install = false) { + function __construct($serveraddr, $user, $pass, $db, $install = false) { $a = get_app(); $stamp1 = microtime(true); + $serveraddr = trim($serveraddr); + + $serverdata = explode(':', $serveraddr); + $server = $serverdata[0]; + + if (count($serverdata) > 1) { + $port = trim($serverdata[1]); + } + $server = trim($server); $user = trim($user); $pass = trim($pass); @@ -55,6 +65,11 @@ class dba { if (class_exists('\PDO') && in_array('mysql', PDO::getAvailableDrivers())) { $this->driver = 'pdo'; $connect = "mysql:host=".$server.";dbname=".$db; + + if (isset($port)) { + $connect .= ";port=".$port; + } + if (isset($a->config["system"]["db_charset"])) { $connect .= ";charset=".$a->config["system"]["db_charset"]; } @@ -64,7 +79,7 @@ class dba { } } elseif (class_exists('mysqli')) { $this->driver = 'mysqli'; - $this->db = @new mysqli($server,$user,$pass,$db); + $this->db = @new mysqli($server, $user, $pass, $db, $port); if (!mysqli_connect_errno()) { $this->connected = true; @@ -74,8 +89,8 @@ class dba { } } elseif (function_exists('mysql_connect')) { $this->driver = 'mysql'; - $this->db = mysql_connect($server,$user,$pass); - if ($this->db && mysql_select_db($db,$this->db)) { + $this->db = mysql_connect($serveraddr, $user, $pass); + if ($this->db && mysql_select_db($db, $this->db)) { $this->connected = true; if (isset($a->config["system"]["db_charset"])) { @@ -484,18 +499,28 @@ class dba { unset($args[0]); // When the second function parameter is an array then use this as the parameter array - if ((count($args) == 1) AND (is_array($args[1]))) { + if ((count($args) > 0) AND (is_array($args[1]))) { $params = $args[1]; - $i = 0; - foreach ($params AS $param) { - $args[++$i] = $param; - } + } else { + $params = $args; + } + + // Renumber the array keys to be sure that they fit + $i = 0; + $args = array(); + foreach ($params AS $param) { + $args[++$i] = $param; } if (!self::$dbo OR !self::$dbo->connected) { return false; } + if (substr_count($sql, '?') != count($args)) { + // Question: Should we continue or stop the query here? + logger('Parameter mismatch. Query "'.$sql.'" - Parameters '.print_r($args, true), LOGGER_DEBUG); + } + $sql = self::$dbo->any_value_fallback($sql); if (x($a->config,'system') && x($a->config['system'], 'db_callstack')) { @@ -520,7 +545,7 @@ class dba { } if (!$stmt->execute()) { - $errorInfo = self::$dbo->db->errorInfo(); + $errorInfo = $stmt->errorInfo(); self::$dbo->error = $errorInfo[2]; self::$dbo->errorno = $errorInfo[1]; $retval = false; @@ -532,8 +557,8 @@ class dba { $stmt = self::$dbo->db->stmt_init(); if (!$stmt->prepare($sql)) { - self::$dbo->error = self::$dbo->db->error; - self::$dbo->errorno = self::$dbo->db->errno; + self::$dbo->error = $stmt->error; + self::$dbo->errorno = $stmt->errno; $retval = false; break; } @@ -553,9 +578,10 @@ class dba { $values[] = &$args[$param]; } - array_unshift($values, $params); - - call_user_func_array(array($stmt, 'bind_param'), $values); + if (count($values) > 0) { + array_unshift($values, $params); + call_user_func_array(array($stmt, 'bind_param'), $values); + } if (!$stmt->execute()) { self::$dbo->error = self::$dbo->db->error; @@ -577,7 +603,8 @@ class dba { } if (self::$dbo->errorno != 0) { - logger('DB Error '.self::$dbo->errorno.': '.self::$dbo->error); + logger('DB Error '.self::$dbo->errorno.': '.self::$dbo->error."\n". + $a->callstack(8))."\n".self::replace_parameters($sql, $args); } $a->save_timestamp($stamp1, 'database'); @@ -754,6 +781,48 @@ class dba { return self::e($sql, $param); } + /** + * @brief Starts a transaction + * + * @return boolean Was the command executed successfully? + */ + static public function transaction() { + if (!self::e('COMMIT')) { + return false; + } + if (!self::e('START TRANSACTION')) { + return false; + } + self::$in_transaction = true; + return true; + } + + /** + * @brief Does a commit + * + * @return boolean Was the command executed successfully? + */ + static public function commit() { + if (!self::e('COMMIT')) { + return false; + } + self::$in_transaction = false; + return true; + } + + /** + * @brief Does a rollback + * + * @return boolean Was the command executed successfully? + */ + static public function rollback() { + if (!self::e('ROLLBACK')) { + return false; + } + self::$in_transaction = false; + return true; + } + /** * @brief Build the array with the table relations * @@ -776,16 +845,16 @@ class dba { } /** - * @brief Insert a row into a table + * @brief Delete a row from a table * * @param string $table Table name * @param array $param parameter array - * @param boolean $in_commit Internal use: Only do a commit after the last delete + * @param boolean $in_process Internal use: Only do a commit after the last delete * @param array $callstack Internal use: prevent endless loops * - * @return boolean|array was the delete successfull? When $in_commit is set: deletion data + * @return boolean|array was the delete successfull? When $in_process is set: deletion data */ - static public function delete($table, $param, $in_commit = false, $callstack = array()) { + static public function delete($table, $param, $in_process = false, &$callstack = array()) { $commands = array(); @@ -797,7 +866,7 @@ class dba { return $commands; } - $callstack[$key] = $key; + $callstack[$key] = true; $table = self::$dbo->escape($table); @@ -814,6 +883,9 @@ class dba { $field = array_keys(self::$relation[$table])[0]; $rel_def = array_values(self::$relation[$table])[0]; + // Create a key for preventing double queries + $qkey = $field.'-'.$table.':'.implode(':', array_keys($param)).':'.implode(':', $param); + // When the search field is the relation field, we don't need to fetch the rows // This is useful when the leading record is already deleted in the frontend but the rest is done in the backend if ((count($param) == 1) AND ($field == array_keys($param)[0])) { @@ -823,7 +895,11 @@ class dba { $commands = array_merge($commands, $retval); } } - } else { + // We quit when this key already exists in the callstack. + } elseif (!isset($callstack[$qkey])) { + + $callstack[$qkey] = true; + // Fetch all rows that are to be deleted $sql = "SELECT ".self::$dbo->escape($field)." FROM `".$table."` WHERE `". implode("` = ? AND `", array_keys($param))."` = ?"; @@ -840,10 +916,13 @@ class dba { } } - if (!$in_commit) { + if (!$in_process) { // Now we finalize the process - self::p("COMMIT"); - self::p("START TRANSACTION"); + $do_transaction = !self::$in_transaction; + + if ($do_transaction) { + self::transaction(); + } $compacted = array(); $counter = array(); @@ -854,8 +933,10 @@ class dba { logger(dba::replace_parameters($sql, $command['param']), LOGGER_DATA); - if (!self::e($sql, $param)) { - self::p("ROLLBACK"); + if (!self::e($sql, $command['param'])) { + if ($do_transaction) { + self::rollback(); + } return false; } } else { @@ -882,14 +963,18 @@ class dba { logger(dba::replace_parameters($sql, $field_values), LOGGER_DATA); - if (!self::e($sql, $param)) { - self::p("ROLLBACK"); + if (!self::e($sql, $field_values)) { + if ($do_transaction) { + self::rollback(); + } return false; } } } } - self::p("COMMIT"); + if ($do_transaction) { + self::commit(); + } return true; } @@ -984,6 +1069,76 @@ class dba { return self::e($sql, $params); } + /** + * @brief Select rows from a table + * + * @param string $table Table name + * @param array $fields array of selected fields + * @param array $condition array of fields for condition + * @param array $params array of several parameters + * + * @return boolean|object If "limit" is equal "1" only a single row is returned, else a query object is returned + * + * Example: + * $table = "item"; + * $fields = array("id", "uri", "uid", "network"); + * $condition = array("uid" => 1, "network" => 'dspr'); + * $params = array("order" => array("id", "received" => true), "limit" => 1); + * + * $data = dba::select($table, $fields, $condition, $params); + */ + static public function select($table, $fields = array(), $condition = array(), $params = array()) { + if ($table == '') { + return false; + } + + if (count($fields) > 0) { + $select_fields = "`".implode("`, `", array_values($fields))."`"; + } else { + $select_fields = "*"; + } + + if (count($condition) > 0) { + $condition_string = " WHERE `".implode("` = ? AND `", array_keys($condition))."` = ?"; + } else { + $condition_string = ""; + } + + $param_string = ''; + $single_row = false; + + if (isset($params['order'])) { + $param_string .= " ORDER BY "; + foreach ($params['order'] AS $fields => $order) { + if (!is_int($fields)) { + $param_string .= "`".$fields."` ".($order ? "DESC" : "ASC").", "; + } else { + $param_string .= "`".$order."`, "; + } + } + $param_string = substr($param_string, 0, -2); + } + + if (isset($params['limit'])) { + if (is_int($params['limit'])) { + $param_string .= " LIMIT ".$params['limit']; + $single_row =($params['limit'] == 1); + } + } + + $sql = "SELECT ".$select_fields." FROM `".$table."`".$condition_string.$param_string; + + $result = self::p($sql, $condition); + + if (is_bool($result) OR !$single_row) { + return $result; + } else { + $row = self::fetch($result); + self::close($result); + return $row; + } + } + /** * @brief Closes the current statement *