mirror of https://github.com/friendica/friendica
WIP
This commit is contained in:
parent
f7ac20e151
commit
c97f07f370
|
@ -6,11 +6,6 @@ use Friendica\Core\Config\Cache\IConfigCache;
|
||||||
use Friendica\Core\Logger;
|
use Friendica\Core\Logger;
|
||||||
use Friendica\Database\Driver\IDriver;
|
use Friendica\Database\Driver\IDriver;
|
||||||
use Friendica\Util\DateTimeFormat;
|
use Friendica\Util\DateTimeFormat;
|
||||||
use Friendica\Util\Profiler;
|
|
||||||
use mysqli_result;
|
|
||||||
use mysqli_stmt;
|
|
||||||
use PDO;
|
|
||||||
use PDOStatement;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class MySQL database class
|
* @class MySQL database class
|
||||||
|
@ -34,22 +29,11 @@ class DBA
|
||||||
* @var IConfigCache
|
* @var IConfigCache
|
||||||
*/
|
*/
|
||||||
private static $configCache;
|
private static $configCache;
|
||||||
/**
|
|
||||||
* @var Profiler
|
|
||||||
*/
|
|
||||||
private static $profiler;
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private static $basePath;
|
|
||||||
private static $connection;
|
private static $connection;
|
||||||
private static $driver;
|
private static $driver;
|
||||||
private static $error = false;
|
private static $error = false;
|
||||||
private static $errorno = 0;
|
private static $errorno = 0;
|
||||||
private static $affected_rows = 0;
|
|
||||||
private static $in_transaction = false;
|
private static $in_transaction = false;
|
||||||
private static $in_retrial = false;
|
|
||||||
private static $relation = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var IDatabase
|
* @var IDatabase
|
||||||
|
@ -183,55 +167,19 @@ class DBA
|
||||||
return self::$db->getDriver()->isConnected(true);
|
return self::$db->getDriver()->isConnected(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Replaces ANY_VALUE() function by MIN() function,
|
|
||||||
* if the database server does not support ANY_VALUE().
|
|
||||||
*
|
|
||||||
* Considerations for Standard SQL, or MySQL with ONLY_FULL_GROUP_BY (default since 5.7.5).
|
|
||||||
* ANY_VALUE() is available from MySQL 5.7.5 https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
|
|
||||||
* A standard fall-back is to use MIN().
|
|
||||||
*
|
|
||||||
* @param string $sql An SQL string without the values
|
|
||||||
* @return string The input SQL string modified if necessary.
|
|
||||||
*/
|
|
||||||
public static function anyValueFallback($sql) {
|
|
||||||
$server_info = self::serverInfo();
|
|
||||||
if (version_compare($server_info, '5.7.5', '<') ||
|
|
||||||
(stripos($server_info, 'MariaDB') !== false)) {
|
|
||||||
$sql = str_ireplace('ANY_VALUE(', 'MIN(', $sql);
|
|
||||||
}
|
|
||||||
return $sql;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief beautifies the query - useful for "SHOW PROCESSLIST"
|
|
||||||
*
|
|
||||||
* This is safe when we bind the parameters later.
|
|
||||||
* The parameter values aren't part of the SQL.
|
|
||||||
*
|
|
||||||
* @param string $sql An SQL string without the values
|
|
||||||
* @return string The input SQL string modified if necessary.
|
|
||||||
*/
|
|
||||||
public static function cleanQuery($sql) {
|
|
||||||
$search = ["\t", "\n", "\r", " "];
|
|
||||||
$replace = [' ', ' ', ' ', ' '];
|
|
||||||
do {
|
|
||||||
$oldsql = $sql;
|
|
||||||
$sql = str_replace($search, $replace, $sql);
|
|
||||||
} while ($oldsql != $sql);
|
|
||||||
|
|
||||||
return $sql;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function p($sql)
|
public static function p($sql)
|
||||||
{
|
{
|
||||||
return self::$db->prepared($sql);
|
$params = Utils::getParameters(func_get_args());
|
||||||
|
|
||||||
|
return self::$db->prepared($sql, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static function e($sql) {
|
public static function e($sql)
|
||||||
|
{
|
||||||
|
$params = Utils::getParameters(func_get_args());
|
||||||
|
|
||||||
|
return self::$db->execute($sql, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -299,13 +247,9 @@ class DBA
|
||||||
return $retval;
|
return $retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function affectedRows()
|
||||||
* @brief Returns the number of affected rows of the last statement
|
{
|
||||||
*
|
return self::$db->getAffectedRows();
|
||||||
* @return int Number of rows
|
|
||||||
*/
|
|
||||||
public static function affectedRows() {
|
|
||||||
return self::$affected_rows;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -48,6 +48,18 @@ class Database implements IDatabase, IDatabaseLock
|
||||||
*/
|
*/
|
||||||
private $dbRelation;
|
private $dbRelation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A possible driver exception of a current call
|
||||||
|
* @var DriverException
|
||||||
|
*/
|
||||||
|
private $currDriverException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of affected rows of a current call
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $currNumRows;
|
||||||
|
|
||||||
public function __construct(IDriver $driver, IConfigCache $configCache, Profiler $profiler, LoggerInterface $logger)
|
public function __construct(IDriver $driver, IConfigCache $configCache, Profiler $profiler, LoggerInterface $logger)
|
||||||
{
|
{
|
||||||
$this->configCache = $configCache;
|
$this->configCache = $configCache;
|
||||||
|
@ -84,6 +96,14 @@ class Database implements IDatabase, IDatabaseLock
|
||||||
return $data[0]['db'];
|
return $data[0]['db'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getAffectedRows()
|
||||||
|
{
|
||||||
|
return $this->currNumRows;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -263,7 +283,7 @@ class Database implements IDatabase, IDatabaseLock
|
||||||
|
|
||||||
if ((count($command['conditions']) > 1) || is_int($first_key)) {
|
if ((count($command['conditions']) > 1) || is_int($first_key)) {
|
||||||
$sql = "DELETE FROM `" . $command['table'] . "`" . $condition_string;
|
$sql = "DELETE FROM `" . $command['table'] . "`" . $condition_string;
|
||||||
$logger->debug(self::replaceParameters($sql, $conditions));
|
$logger->debug($driver->replaceParameters($sql, $conditions));
|
||||||
|
|
||||||
if (!$this->execute($sql, $conditions)) {
|
if (!$this->execute($sql, $conditions)) {
|
||||||
if ($do_transaction) {
|
if ($do_transaction) {
|
||||||
|
@ -293,7 +313,7 @@ class Database implements IDatabase, IDatabaseLock
|
||||||
$sql = "DELETE FROM `" . $table . "` WHERE `" . $field . "` IN (" .
|
$sql = "DELETE FROM `" . $table . "` WHERE `" . $field . "` IN (" .
|
||||||
substr(str_repeat("?, ", count($field_values)), 0, -2) . ");";
|
substr(str_repeat("?, ", count($field_values)), 0, -2) . ");";
|
||||||
|
|
||||||
$logger->debug(self::replaceParameters($sql, $field_values));
|
$logger->debug($driver->replaceParameters($sql, $field_values));
|
||||||
|
|
||||||
if (!$this->execute($sql, $field_values)) {
|
if (!$this->execute($sql, $field_values)) {
|
||||||
if ($do_transaction) {
|
if ($do_transaction) {
|
||||||
|
@ -424,8 +444,10 @@ class Database implements IDatabase, IDatabaseLock
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @param bool $retried if true, this is a retry of the current call
|
||||||
*/
|
*/
|
||||||
public function prepared($sql)
|
public function prepared($sql, array $params = [], $retried = false)
|
||||||
{
|
{
|
||||||
$logger = $this->logger;
|
$logger = $this->logger;
|
||||||
$profiler = $this->profiler;
|
$profiler = $this->profiler;
|
||||||
|
@ -434,8 +456,6 @@ class Database implements IDatabase, IDatabaseLock
|
||||||
|
|
||||||
$stamp1 = microtime(true);
|
$stamp1 = microtime(true);
|
||||||
|
|
||||||
$params = $this->getParameters(func_get_args());
|
|
||||||
|
|
||||||
// Renumber the array keys to be sure that they fit
|
// Renumber the array keys to be sure that they fit
|
||||||
$i = 0;
|
$i = 0;
|
||||||
$args = [];
|
$args = [];
|
||||||
|
@ -463,9 +483,8 @@ class Database implements IDatabase, IDatabaseLock
|
||||||
$sql = "/*".System::callstack()." */ ".$sql;
|
$sql = "/*".System::callstack()." */ ".$sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
self::$error = '';
|
$this->currDriverException = null;
|
||||||
self::$errorno = 0;
|
$this->currNumRows = 0;
|
||||||
self::$affected_rows = 0;
|
|
||||||
|
|
||||||
// We have to make some things different if this function is called from "e"
|
// We have to make some things different if this function is called from "e"
|
||||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||||
|
@ -482,40 +501,51 @@ class Database implements IDatabase, IDatabaseLock
|
||||||
try {
|
try {
|
||||||
|
|
||||||
$retval = $driver->executePrepared($sql, $args);
|
$retval = $driver->executePrepared($sql, $args);
|
||||||
self::$affected_rows = $driver->getNumRows($retval);
|
$this->currNumRows = $driver->getNumRows($retval);
|
||||||
|
|
||||||
} catch (DriverException $exception) {
|
} catch (DriverException $exception) {
|
||||||
// We are having an own error logging in the function "e"
|
// We are having an own error logging in the function "e"
|
||||||
if (($exception->getCode() != 0) && !$called_from_e) {
|
if (($exception->getCode() != 0) && !$called_from_e) {
|
||||||
// We have to preserve the error code, somewhere in the logging it get lost
|
|
||||||
$error = $exception->getMessage();
|
|
||||||
$errorno = $exception->getCode();
|
|
||||||
|
|
||||||
$this->logger->error('DB Error ' . self::$errorno . ': ' . self::$error . "\n" .
|
// We have to preserve the error code, somewhere in the logging it get lost
|
||||||
System::callstack(8) . "\n" . self::replaceParameters($sql, $args));
|
$this->currDriverException = $exception;
|
||||||
|
|
||||||
|
$this->logger->error('DB Error', [
|
||||||
|
'code' => $exception->getCode(),
|
||||||
|
'error' => $exception->getMessage(),
|
||||||
|
'callstack' => System::callstack(8),
|
||||||
|
'param ' => $driver->replaceParameters($sql, $args),
|
||||||
|
]);
|
||||||
|
|
||||||
// On a lost connection we try to reconnect - but only once.
|
// On a lost connection we try to reconnect - but only once.
|
||||||
if ($errorno == 2006) {
|
if ($exception->getCode() == 2006) {
|
||||||
if (self::$in_retrial || !$driver->reconnect()) {
|
if ($retried || !$driver->reconnect()) {
|
||||||
// It doesn't make sense to continue when the database connection was lost
|
// It doesn't make sense to continue when the database connection was lost
|
||||||
if (self::$in_retrial) {
|
if ($retried) {
|
||||||
$logger->notice('Giving up retrial because of database error ' . $errorno . ': ' . $error);
|
$logger->notice('Giving up retrial because of database error',
|
||||||
|
[
|
||||||
|
'code' => $exception->getCode(),
|
||||||
|
'error' => $exception->getMessage(),
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
$logger->notice("Couldn't reconnect after database error " . $errorno . ': ' . $error);
|
$logger->notice("Couldn't reconnect after database error",
|
||||||
|
[
|
||||||
|
'code' => $exception->getCode(),
|
||||||
|
'error' => $exception->getMessage(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
} else {
|
} else {
|
||||||
// We try it again
|
// We try it again
|
||||||
$logger->notice('Reconnected after database error ' . $errorno . ': ' . $error);
|
$logger->notice('Reconnected after database error',
|
||||||
self::$in_retrial = true;
|
[
|
||||||
$ret = $this->prepared($sql, $args);
|
'code' => $exception->getCode(),
|
||||||
self::$in_retrial = false;
|
'error' => $exception->getMessage(),
|
||||||
return $ret;
|
]);
|
||||||
|
return $this->prepared($sql, $args, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self::$error = $error;
|
|
||||||
self::$errorno = $errorno;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,7 +562,7 @@ class Database implements IDatabase, IDatabaseLock
|
||||||
@file_put_contents($config->get('system', 'db_log'), DateTimeFormat::utcNow() . $duration . "\t" .
|
@file_put_contents($config->get('system', 'db_log'), DateTimeFormat::utcNow() . $duration . "\t" .
|
||||||
basename($backtrace[1]["file"])."\t" .
|
basename($backtrace[1]["file"])."\t" .
|
||||||
$backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t" .
|
$backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t" .
|
||||||
substr(self::replaceParameters($sql, $args), 0, 2000)."\n", FILE_APPEND);
|
substr($driver->replaceParameters($sql, $args), 0, 2000)."\n", FILE_APPEND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,21 +572,19 @@ class Database implements IDatabase, IDatabaseLock
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function execute($sql)
|
public function execute($sql, array $params = [])
|
||||||
{
|
{
|
||||||
$profiler = $this->profiler;
|
$profiler = $this->profiler;
|
||||||
$logger = $this->logger;
|
$logger = $this->logger;
|
||||||
|
|
||||||
$stamp = microtime(true);
|
$stamp = microtime(true);
|
||||||
|
|
||||||
$params = self::getParameters(func_get_args());
|
|
||||||
|
|
||||||
// In a case of a deadlock we are repeating the query 20 times
|
// In a case of a deadlock we are repeating the query 20 times
|
||||||
$timeout = 20;
|
$timeout = 20;
|
||||||
$errorno = 0;
|
$errorno = 0;
|
||||||
|
$retval = false;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try {
|
|
||||||
$stmt = $this->prepared($sql, $params);
|
$stmt = $this->prepared($sql, $params);
|
||||||
|
|
||||||
if (is_bool($stmt)) {
|
if (is_bool($stmt)) {
|
||||||
|
@ -568,29 +596,32 @@ class Database implements IDatabase, IDatabaseLock
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->close($stmt);
|
$this->close($stmt);
|
||||||
} catch (DriverException $exception) {
|
|
||||||
$errorno = $exception->getCode();
|
$errorno = isset($this->currDriverException) ? $this->currDriverException->getCode() : 0;
|
||||||
}
|
|
||||||
|
|
||||||
} while (($errorno == 1213) && (--$timeout > 0));
|
} while (($errorno == 1213) && (--$timeout > 0));
|
||||||
|
|
||||||
if ($errorno != 0) {
|
if ($errorno != 0) {
|
||||||
// We have to preserve the error code, somewhere in the logging it get lost
|
// We have to preserve the error code, somewhere in the logging it get lost
|
||||||
$error = self::$error;
|
$exception = $this->currDriverException;
|
||||||
$errorno = self::$errorno;
|
|
||||||
|
|
||||||
$logger->error('DB Error ' . self::$errorno . ': ' . self::$error . "\n" .
|
$this->logger->error('DB Error', [
|
||||||
System::callstack(8)."\n".self::replaceParameters($sql, $params));
|
'code' => $exception->getCode(),
|
||||||
|
'error' => $exception->getMessage(),
|
||||||
|
'callstack' => System::callstack(8),
|
||||||
|
'param ' => $this->driver->replaceParameters($sql, $params),
|
||||||
|
]);
|
||||||
|
|
||||||
// On a lost connection we simply quit.
|
// On a lost connection we simply quit.
|
||||||
// A reconnect like in self::p could be dangerous with modifications
|
// A reconnect like in $this->prepared() could be dangerous with modifications
|
||||||
if ($errorno == 2006) {
|
if ($errorno == 2006) {
|
||||||
$logger->notice('Giving up because of database error '.$errorno.': '.$error);
|
$logger->notice('Giving up retrial because of database error',
|
||||||
|
[
|
||||||
|
'code' => $exception->getCode(),
|
||||||
|
'error' => $exception->getMessage(),
|
||||||
|
]);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
self::$error = $error;
|
|
||||||
self::$errorno = $errorno;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$profiler->saveTimestamp($stamp, "database_write", System::callstack());
|
$profiler->saveTimestamp($stamp, "database_write", System::callstack());
|
||||||
|
@ -598,54 +629,6 @@ class Database implements IDatabase, IDatabaseLock
|
||||||
return $retval;
|
return $retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Replaces the ? placeholders with the parameters in the $args array
|
|
||||||
*
|
|
||||||
* @param string $sql SQL query
|
|
||||||
* @param array $args The parameters that are to replace the ? placeholders
|
|
||||||
*
|
|
||||||
* @return string The replaced SQL query
|
|
||||||
*/
|
|
||||||
public static function replaceParameters($sql, array $args)
|
|
||||||
{
|
|
||||||
$offset = 0;
|
|
||||||
|
|
||||||
foreach ($args AS $param => $value) {
|
|
||||||
if (is_int($args[$param]) || is_float($args[$param])) {
|
|
||||||
$replace = intval($args[$param]);
|
|
||||||
} else {
|
|
||||||
$replace = "'" . $this->escape($args[$param]) . "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
$pos = strpos($sql, '?', $offset);
|
|
||||||
if ($pos !== false) {
|
|
||||||
$sql = substr_replace($sql, $replace, $pos, 1);
|
|
||||||
}
|
|
||||||
$offset = $pos + strlen($replace);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sql;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert parameter array to an universal form
|
|
||||||
*
|
|
||||||
* @param array $args Parameter array
|
|
||||||
*
|
|
||||||
* @return array universalized parameter array
|
|
||||||
*/
|
|
||||||
public static function getParameters(array $args)
|
|
||||||
{
|
|
||||||
unset($args[0]);
|
|
||||||
|
|
||||||
// When the second function parameter is an array then use this as the parameter array
|
|
||||||
if ((count($args) > 0) && (is_array($args[1]))) {
|
|
||||||
return $args[1];
|
|
||||||
} else {
|
|
||||||
return $args;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the SQL condition string built from the provided condition array
|
* @brief Returns the SQL condition string built from the provided condition array
|
||||||
*
|
*
|
||||||
|
|
|
@ -53,4 +53,28 @@ abstract class AbstractDriver implements IDriver
|
||||||
// fallback, if no explicit escaping is set for a connection
|
// fallback, if no explicit escaping is set for a connection
|
||||||
return str_replace("'", "\\'", $sql);
|
return str_replace("'", "\\'", $sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function replaceParameters($sql, array $args = [])
|
||||||
|
{
|
||||||
|
$offset = 0;
|
||||||
|
|
||||||
|
foreach ($args AS $param => $value) {
|
||||||
|
if (is_int($args[$param]) || is_float($args[$param])) {
|
||||||
|
$replace = intval($args[$param]);
|
||||||
|
} else {
|
||||||
|
$replace = "'" . $this->escape($args[$param]) . "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
$pos = strpos($sql, '?', $offset);
|
||||||
|
if ($pos !== false) {
|
||||||
|
$sql = substr_replace($sql, $replace, $pos, 1);
|
||||||
|
}
|
||||||
|
$offset = $pos + strlen($replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sql;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,16 @@ interface IDriver
|
||||||
*/
|
*/
|
||||||
function escape($sql);
|
function escape($sql);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the ? placeholders with the parameters in the $args array
|
||||||
|
*
|
||||||
|
* @param string $sql SQL query
|
||||||
|
* @param array $args The parameters that are to replace the ? placeholders
|
||||||
|
*
|
||||||
|
* @return string The replaced SQL query
|
||||||
|
*/
|
||||||
|
function replaceParameters($sql, array $args = []);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the current statement
|
* Closes the current statement
|
||||||
*
|
*
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace Friendica\Database\Driver;
|
namespace Friendica\Database\Driver;
|
||||||
|
|
||||||
use Friendica\Database\Database;
|
|
||||||
use mysqli;
|
use mysqli;
|
||||||
use mysqli_result;
|
use mysqli_result;
|
||||||
use mysqli_stmt;
|
use mysqli_stmt;
|
||||||
|
@ -190,7 +189,7 @@ class MySQLiDriver extends AbstractDriver implements IDriver
|
||||||
// The fallback routine is called as well when there are no arguments
|
// The fallback routine is called as well when there are no arguments
|
||||||
if (!$can_be_prepared || (count($args) == 0)) {
|
if (!$can_be_prepared || (count($args) == 0)) {
|
||||||
|
|
||||||
$retval = $this->connection->query(Database::replaceParameters($sql, $args));
|
$retval = $this->connection->query($this->replaceParameters($sql, $args));
|
||||||
|
|
||||||
if ($this->connection->errno) {
|
if ($this->connection->errno) {
|
||||||
throw new DriverException($this->connection->error, $this->connection->errno);
|
throw new DriverException($this->connection->error, $this->connection->errno);
|
||||||
|
|
|
@ -26,6 +26,13 @@ interface IDatabase
|
||||||
*/
|
*/
|
||||||
function getDatabaseName();
|
function getDatabaseName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of affected rows of the last statement
|
||||||
|
*
|
||||||
|
* @return int Number of rows
|
||||||
|
*/
|
||||||
|
function getAffectedRows();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes a prepared statement that returns data
|
* Executes a prepared statement that returns data
|
||||||
*
|
*
|
||||||
|
@ -34,13 +41,12 @@ interface IDatabase
|
||||||
* Please only use it with complicated queries.
|
* Please only use it with complicated queries.
|
||||||
* For all regular queries please use DBA::select or DBA::exists
|
* For all regular queries please use DBA::select or DBA::exists
|
||||||
*
|
*
|
||||||
* @param string $sql SQL statement
|
* @param string $sql SQL statement
|
||||||
|
* @param array $params The parameters of the current SQL statement
|
||||||
*
|
*
|
||||||
* @return bool|object statement object or result object
|
* @return bool|object statement object or result object
|
||||||
*
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
function prepared($sql);
|
function prepared($sql, array $params = []);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes a prepared statement like UPDATE or INSERT that doesn't return data
|
* Executes a prepared statement like UPDATE or INSERT that doesn't return data
|
||||||
|
@ -48,10 +54,11 @@ interface IDatabase
|
||||||
* Please use DBA::delete, DBA::insert, DBA::update, ... instead
|
* Please use DBA::delete, DBA::insert, DBA::update, ... instead
|
||||||
*
|
*
|
||||||
* @param string $sql SQL statement
|
* @param string $sql SQL statement
|
||||||
|
* @param array $params The parameters of the current SQL statement
|
||||||
|
*
|
||||||
* @return boolean Was the query successfull? False is returned only if an error occurred
|
* @return boolean Was the query successfull? False is returned only if an error occurred
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
function execute($sql);
|
function execute($sql, array $params = []);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if data exists
|
* Check if data exists
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Friendica\Database;
|
||||||
|
|
||||||
|
class Utils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Convert parameter array to an universal form
|
||||||
|
*
|
||||||
|
* @param array $args Parameter array
|
||||||
|
*
|
||||||
|
* @return array universalized parameter array
|
||||||
|
*/
|
||||||
|
public static function getParameters(array $args)
|
||||||
|
{
|
||||||
|
unset($args[0]);
|
||||||
|
|
||||||
|
// When the second function parameter is an array then use this as the parameter array
|
||||||
|
if ((count($args) > 0) && (is_array($args[1]))) {
|
||||||
|
return $args[1];
|
||||||
|
} else {
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Replaces ANY_VALUE() function by MIN() function,
|
||||||
|
* if the database server does not support ANY_VALUE().
|
||||||
|
*
|
||||||
|
* Considerations for Standard SQL, or MySQL with ONLY_FULL_GROUP_BY (default since 5.7.5).
|
||||||
|
* ANY_VALUE() is available from MySQL 5.7.5 https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
|
||||||
|
* A standard fall-back is to use MIN().
|
||||||
|
*
|
||||||
|
* @param string $sql An SQL string without the values
|
||||||
|
* @return string The input SQL string modified if necessary.
|
||||||
|
*/
|
||||||
|
public static function anyValueFallback($sql) {
|
||||||
|
$server_info = self::serverInfo();
|
||||||
|
if (version_compare($server_info, '5.7.5', '<') ||
|
||||||
|
(stripos($server_info, 'MariaDB') !== false)) {
|
||||||
|
$sql = str_ireplace('ANY_VALUE(', 'MIN(', $sql);
|
||||||
|
}
|
||||||
|
return $sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief beautifies the query - useful for "SHOW PROCESSLIST"
|
||||||
|
*
|
||||||
|
* This is safe when we bind the parameters later.
|
||||||
|
* The parameter values aren't part of the SQL.
|
||||||
|
*
|
||||||
|
* @param string $sql An SQL string without the values
|
||||||
|
* @return string The input SQL string modified if necessary.
|
||||||
|
*/
|
||||||
|
public static function cleanQuery($sql) {
|
||||||
|
$search = ["\t", "\n", "\r", " "];
|
||||||
|
$replace = [' ', ' ', ' ', ' '];
|
||||||
|
do {
|
||||||
|
$oldsql = $sql;
|
||||||
|
$sql = str_replace($search, $replace, $sql);
|
||||||
|
} while ($oldsql != $sql);
|
||||||
|
|
||||||
|
return $sql;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue