Merge pull request #11 from forkbb/db

Change DB and DBStatement
This commit is contained in:
Visman 2021-12-23 17:11:03 +07:00 committed by GitHub
commit 7b922096f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 311 additions and 314 deletions

View file

@ -10,14 +10,18 @@ declare(strict_types=1);
namespace ForkBB\Core;
use ForkBB\Core\DBStatement;
use ForkBB\Core\DB\DBStatement;
use PDO;
use PDOStatement;
use PDOException;
use ReturnTypeWillChange;
class DB extends PDO
class DB
{
/**
* @var PDO
*/
protected $pdo;
/**
* Префикс для таблиц базы
* @var string
@ -30,12 +34,24 @@ class DB extends PDO
*/
protected $dbType;
/**
* Имя класса для драйвера
* @var string
*/
protected $dbDrvClass;
/**
* Драйвер текущей базы
* @var //????
*/
protected $dbDrv;
/**
* Имя класса для PDOStatement
* @var string
*/
protected $statementClass;
/**
* Количество выполненных запросов
* @var int
@ -54,93 +70,129 @@ class DB extends PDO
*/
protected $delta = 0;
/**
* @var array
*/
protected $pdoMethods = [
'beginTransaction' => true,
'commit' => true,
'errorCode' => true,
'errorInfo' => true,
'exec' => true,
'getAttribute' => true,
'getAvailableDrivers' => true,
'inTransaction' => true,
'lastInsertId' => true,
'prepare' => true,
'query' => true,
'quote' => true,
'rollBack' => true,
'setAttribute' => true,
'pgsqlCopyFromArray' => true,
'pgsqlCopyFromFile' => true,
'pgsqlCopyToArray' => true,
'pgsqlCopyToFile' => true,
'pgsqlGetNotify' => true,
'pgsqlGetPid' => true,
'pgsqlLOBCreate' => true,
'pgsqlLOBOpen' => true,
'pgsqlLOBUnlink' => true,
'sqliteCreateAggregate' => true,
'sqliteCreateCollation' => true,
'sqliteCreateFunction' => true,
];
public function __construct(string $dsn, string $username = null, string $password = null, array $options = [], string $prefix = '')
{
$type = \strstr($dsn, ':', true);
$typeU = \ucfirst($type);
$dsn = $this->initialConfig($dsn);
$this->dbPrefix = $prefix;
list($initSQLCommands, $initFunction) = $this->prepareOptions($options);
$start = \microtime(true);
$this->pdo = new PDO($dsn, $username, $password, $options);
$this->saveQuery('PDO::__construct()', \microtime(true) - $start, false);
if (\is_string($initSQLCommands)) {
$this->exec($initSQLCommands);
}
if (
! $type
|| ! \in_array($type, PDO::getAvailableDrivers(), true)
|| ! \is_file(__DIR__ . "/DB/{$typeU}.php")
null !== $initFunction
&& true !== $initFunction($this)
) {
throw new PDOException("initFunction failure");
}
$this->beginTransaction();
}
protected function initialConfig(string $dsn): string
{
$type = \strstr($dsn, ':', true);
if (! \in_array($type, PDO::getAvailableDrivers(), true)) {
throw new PDOException("PDO does not have driver for '{$type}'");
}
$typeU = \ucfirst($type);
if (! \is_file(__DIR__ . "/DB/{$typeU}.php")) {
throw new PDOException("Driver isn't found for '$type'");
}
$statement = $typeU . 'Statement' . (\PHP_MAJOR_VERSION < 8 ? '7' : '');
$this->dbType = $type;
$this->dbDrvClass = "ForkBB\\Core\\DB\\{$typeU}";
if (\is_file(__DIR__ . "/DB/{$statement}.php")) {
$statement = 'ForkBB\\Core\\DB\\' . $statement;
if (\is_file(__DIR__ . "/DB/{$typeU}Statement.php")) {
$this->statementClass = "ForkBB\\Core\\DB\\{$typeU}Statement";
} else {
$statement = DBStatement::class;
$this->statementClass = DBStatement::class;
}
if ('sqlite' === $type) {
$dsn = \str_replace('!PATH!', \realpath(__DIR__ . '/../config/db') . '/', $dsn);
}
$this->dbType = $type;
$this->dbPrefix = $prefix;
return $dsn;
}
protected function prepareOptions(array &$options): array
{
$result = [
0 => null,
1 => null,
];
if (isset($options['initSQLCommands'])) {
$initSQLCommands = \implode(';', $options['initSQLCommands']);
$result[0] = \implode(';', $options['initSQLCommands']);
unset($options['initSQLCommands']);
} else {
$initSQLCommands = null;
}
if (isset($options['initFunction'])) {
$result[1] = $options['initFunction'];
unset($options['initFunction']);
}
$options += [
self::ATTR_DEFAULT_FETCH_MODE => self::FETCH_ASSOC,
self::ATTR_EMULATE_PREPARES => false,
self::ATTR_STRINGIFY_FETCHES => false,
self::ATTR_ERRMODE => self::ERRMODE_EXCEPTION,
self::ATTR_STATEMENT_CLASS => [$statement, [$this]],
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
];
$start = \microtime(true);
parent::__construct($dsn, $username, $password, $options);
$this->saveQuery('PDO::__construct()', \microtime(true) - $start, false);
if ($initSQLCommands) {
$this->exec($initSQLCommands);
}
$this->beginTransaction();
return $result;
}
/**
* Передает вызовы методов в драйвер текущей базы
*/
public function __call(string $name, array $args) /* : mixed */
protected function dbStatement(PDOStatement $stmt): DBStatement
{
if (empty($this->dbDrv)) {
$drv = 'ForkBB\\Core\\DB\\' . \ucfirst($this->dbType);
$this->dbDrv = new $drv($this, $this->dbPrefix);
}
return $this->dbDrv->$name(...$args);
}
/**
* Метод определяет массив ли опций подан на вход
*/
protected function isOptions(array $options): bool
{
$verify = [self::ATTR_CURSOR => [self::CURSOR_FWDONLY, self::CURSOR_SCROLL]];
foreach ($options as $key => $value) {
if (
! isset($verify[$key])
|| ! \in_array($value, $verify[$key], true)
) {
return false;
}
}
return true;
return new $this->statementClass($this, $stmt);
}
/**
@ -207,7 +259,7 @@ class DB extends PDO
/**
* Метод возвращает значение из массива параметров по ключу или исключение
*/
public function getValue(/* mixed */ $key, array $params) /* : mixed */
public function getValue(/* int|string */ $key, array $params) /* : mixed */
{
if (
\is_string($key)
@ -259,6 +311,7 @@ class DB extends PDO
if ($add) {
++$this->qCount;
}
$this->queries[] = [$query, $time + $this->delta];
$this->delta = 0;
}
@ -266,23 +319,29 @@ class DB extends PDO
/**
* Метод расширяет PDO::exec()
*/
#[ReturnTypeWillChange]
public function exec(/* string */ $query, array $params = []) /* : int|false */
public function exec(string $query, array $params = []) /* : int|false */
{
$map = $this->parse($query, $params);
if (empty($params)) {
$start = \microtime(true);
$result = parent::exec($query);
$result = $this->pdo->exec($query);
$this->saveQuery($query, \microtime(true) - $start);
return $result;
}
$start = \microtime(true);
$stmt = parent::prepare($query);
$stmt = $this->pdo->prepare($query);
$this->delta = \microtime(true) - $start;
if (! $stmt instanceof PDOStatement) {
return false;
}
$stmt = $this->dbStatement($stmt);
$stmt->setMap($map);
if ($stmt->execute($params)) {
@ -295,31 +354,20 @@ class DB extends PDO
/**
* Метод расширяет PDO::prepare()
*/
#[ReturnTypeWillChange]
public function prepare(/* string */ $query, /* array */ $arg1 = null, /* array */ $arg2 = null): PDOStatement
public function prepare(string $query, array $params = [], array $options = []) /* : DBStatement|false */
{
if (
empty($arg1) === empty($arg2)
|| ! empty($arg2)
) {
$params = $arg1;
$options = $arg2;
} elseif ($this->isOptions($arg1)) {
$params = [];
$options = $arg1;
} else {
$params = $arg1;
$options = [];
}
$map = $this->parse($query, $params);
$map = $this->parse($query, $params);
$start = \microtime(true);
$stmt = parent::prepare($query, $options);
$stmt = $this->pdo->prepare($query, $options);
$this->delta = \microtime(true) - $start;
$stmt->setMap($map);
if (! $stmt instanceof PDOStatement) {
return false;
}
$stmt = $this->dbStatement($stmt);
$stmt->setMap($map);
$stmt->bindValueList($params);
return $stmt;
@ -328,8 +376,7 @@ class DB extends PDO
/**
* Метод расширяет PDO::query()
*/
#[ReturnTypeWillChange]
public function query(string $query, /* mixed */ ...$args) /* : PDOStatement|false */
public function query(string $query, /* mixed */ ...$args) /* : DBStatement|false */
{
if (
isset($args[0])
@ -343,17 +390,28 @@ class DB extends PDO
$map = $this->parse($query, $params);
if (empty($params)) {
$start = \microtime(true);
$result = parent::query($query, ...$args);
$start = \microtime(true);
$stmt = $this->pdo->query($query, ...$args);
$this->saveQuery($query, \microtime(true) - $start);
return $result;
if (! $stmt instanceof PDOStatement) {
return false;
}
return $this->dbStatement($stmt);
}
$start = \microtime(true);
$stmt = parent::prepare($query);
$stmt = $this->pdo->prepare($query);
$this->delta = \microtime(true) - $start;
if (! $stmt instanceof PDOStatement) {
return false;
}
$stmt = $this->dbStatement($stmt);
$stmt->setMap($map);
if ($stmt->execute($params)) {
@ -373,7 +431,8 @@ class DB extends PDO
public function beginTransaction(): bool
{
$start = \microtime(true);
$result = parent::beginTransaction();
$result = $this->pdo->beginTransaction();
$this->saveQuery('beginTransaction()', \microtime(true) - $start, false);
return $result;
@ -385,7 +444,8 @@ class DB extends PDO
public function commit(): bool
{
$start = \microtime(true);
$result = parent::commit();
$result = $this->pdo->commit();
$this->saveQuery('commit()', \microtime(true) - $start, false);
return $result;
@ -397,9 +457,26 @@ class DB extends PDO
public function rollback(): bool
{
$start = \microtime(true);
$result = parent::rollback();
$result = $this->pdo->rollback();
$this->saveQuery('rollback()', \microtime(true) - $start, false);
return $result;
}
/**
* Передает вызовы метода в PDO или драйвер текущей базы
*/
public function __call(string $name, array $args) /* : mixed */
{
if (isset($this->pdoMethods[$name])) {
return $this->pdo->$name(...$args);
} elseif (empty($this->dbDrv)) {
$this->dbDrv = new $this->dbDrvClass($this, $this->dbPrefix);
// ????? проверка типа
}
return $this->dbDrv->$name(...$args);
}
}

View file

@ -1,88 +0,0 @@
<?php
/**
* This file is part of the ForkBB <https://github.com/forkbb>.
*
* @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
* @license The MIT License (MIT)
*/
declare(strict_types=1);
namespace ForkBB\Core\DB;
use ForkBB\Core\DB\AbstractStatement;
use PDO;
abstract class AbstractSqliteStatement extends AbstractStatement
{
/**
* https://github.com/php/php-src/blob/master/ext/pdo_sqlite/sqlite_statement.c
*
* SQLite:
* native_type:
* null - для значения NULL, а не типа столбца
* integer - это INTEGER, NUMERIC(?), BOOLEAN // BOOLEAN тут как-то не к месту, его бы в отдельный тип
* string - это TEXT
* double - это REAL, NUMERIC(?) // NUMERIC может быть и double, и integer
* sqlite:decl_type:
* INTEGER
* TEXT
* REAL
* NUMERIC
* BOOLEAN
* ... (это те типы, которые прописаны в CREATE TABLE и полученные после перекодировки из {driver}::bTypeRepl)
*/
/**
* @var array
*/
protected $nativeTypeRepl = [
'integer' => self::INTEGER,
'double' => self::FLOAT,
];
public function getColumnsType(): array
{
if (isset($this->columnsType)) {
return $this->columnsType;
}
$this->columnsType = [];
$count = $this->columnCount();
$i = 0;
// $dbType = $this->db->getType();
for ($i = 0; $i < $count; $i++) {
$meta = $this->getColumnMeta($i);
$type = null;
// $declType = $meta[$dbType . ':decl_type'] ?? null;
$declType = $meta['sqlite:decl_type'] ?? null;
if (null === $declType) {
$type = $this->nativeTypeRepl[$meta['native_type']] ?? null;
} elseif (\preg_match('%INT%i', $declType)) {
$type = self::INTEGER;
} elseif (\preg_match('%BOOL%i', $declType)) {
$type = self::BOOLEAN;
// } elseif (\preg_match('%REAL|FLOA|DOUB|NUMERIC|DECIMAL%i', $declType)) {
// $type = self::FLOAT;
}
if ($type) {
$this->columnsType[$i] = $type;
if (isset($meta['name'])) { // ????? проверка на тип содержимого? только строки, не числа?
$this->columnsType[$meta['name']] = $type;
}
}
}
return $this->columnsType;
}
protected function convToBoolean(/* mixed */ $value): bool
{
return (bool) $value;
}
}

View file

@ -10,7 +10,7 @@ declare(strict_types=1);
namespace ForkBB\Core\DB;
use ForkBB\Core\DBStatement;
use ForkBB\Core\DB\DBStatement;
use PDO;
use PDOStatement;
use PDOException;
@ -68,14 +68,14 @@ abstract class AbstractStatement extends DBStatement
}
}
protected function dbSetFetchMode(int $mode, ...$args): bool
public function setFetchMode(int $mode, ...$args): bool
{
$this->setFetchVars($mode, ...$args);
return parent::setFetchMode($mode, ...$args);
return $this->stmt->setFetchMode($mode, ...$args);
}
protected function dbFetch(int $mode = 0, int $orientation = PDO::FETCH_ORI_NEXT, int $offset = 0) /* : mixed */
public function fetch(int $mode = 0, int $orientation = PDO::FETCH_ORI_NEXT, int $offset = 0) /* : mixed */
{
$this->okFetchColumn = false;
@ -86,7 +86,7 @@ abstract class AbstractStatement extends DBStatement
$colNum = 0;
}
$data = parent::fetch(
$data = $this->stmt->fetch(
PDO::FETCH_COLUMN === $mode ? PDO::FETCH_NUM : $mode,
$orientation,
$offset
@ -130,7 +130,7 @@ abstract class AbstractStatement extends DBStatement
return $data;
}
protected function dbFetchAll(int $mode, ...$args): array
public function fetchAll(int $mode = 0, ...$args): array
{
if (0 !== $mode) {
$this->setFetchVars($mode, ...$args);
@ -144,7 +144,7 @@ abstract class AbstractStatement extends DBStatement
case PDO::FETCH_NUM:
case PDO::FETCH_ASSOC:
case PDO::FETCH_COLUMN:
while (false !== ($data = $this->dbFetch()) || $this->okFetchColumn) {
while (false !== ($data = $this->fetch()) || $this->okFetchColumn) {
$result[] = $data;
}
@ -154,7 +154,7 @@ abstract class AbstractStatement extends DBStatement
throw new PDOException('General error: PDO::FETCH_KEY_PAIR fetch mode requires the result set to contain exactly 2 columns');
}
while (false !== ($data = $this->dbFetch(PDO::FETCH_NUM))) {
while (false !== ($data = $this->fetch(PDO::FETCH_NUM))) {
$result[$data[0]] = $data[1];
}
@ -165,7 +165,7 @@ abstract class AbstractStatement extends DBStatement
case PDO::FETCH_UNIQUE | PDO::FETCH_ASSOC:
$this->fetchMode ^= PDO::FETCH_UNIQUE;
while (false !== ($data = $this->dbFetch())) {
while (false !== ($data = $this->fetch())) {
$key = \array_shift($data);
$result[$key] = $data;
}
@ -177,7 +177,7 @@ abstract class AbstractStatement extends DBStatement
case PDO::FETCH_GROUP | PDO::FETCH_ASSOC:
$this->fetchMode ^= PDO::FETCH_GROUP;
while (false !== ($data = $this->dbFetch())) {
while (false !== ($data = $this->fetch())) {
$key = \array_shift($data);
if (PDO::FETCH_BOTH === $this->fetchMode) {
@ -195,10 +195,9 @@ abstract class AbstractStatement extends DBStatement
default:
throw new PDOException('AbstractStatement class does not support this type for fetchAll(): ' . $this->fetchMode);
return parent::fetchAll($mode, ...$args);
return $this->stmt->fetchAll($mode, ...$args);
}
return $result;
}
}

View file

@ -8,13 +8,14 @@
declare(strict_types=1);
namespace ForkBB\Core;
namespace ForkBB\Core\DB;
use ForkBB\Core\DB;
use PDO;
use PDOStatement;
use PDOException;
class DBStatement extends PDOStatement
class DBStatement
{
const BOOLEAN = 'b';
const FLOAT = 'f';
@ -22,11 +23,15 @@ class DBStatement extends PDOStatement
const STRING = 's';
/**
* Префикс для таблиц базы
* @var PDO
* @var DB
*/
protected $db;
/**
* @var PDOStatement
*/
protected $stmt;
/**
* Карта преобразования переменных
* @var array
@ -47,9 +52,10 @@ class DBStatement extends PDOStatement
'as' => PDO::PARAM_STR,
];
protected function __construct(PDO $db)
public function __construct(DB $db, PDOStatement $stmt)
{
$this->db = $db;
$this->db = $db;
$this->stmt = $stmt;
}
/**
@ -74,12 +80,13 @@ class DBStatement extends PDOStatement
if (! \is_array($bValue)) {
throw new PDOException("Expected array: key='{$key}'");
}
foreach ($data as $bParam) {
parent::bindValue($bParam, \array_shift($bValue), $bType); //????
$this->stmt->bindValue($bParam, \array_shift($bValue), $bType); //????
}
} else {
foreach ($data as $bParam) {
parent::bindValue($bParam, $bValue, $bType); //????
$this->stmt->bindValue($bParam, $bValue, $bType); //????
}
}
}
@ -88,7 +95,7 @@ class DBStatement extends PDOStatement
/**
* Метод расширяет PDOStatement::execute()
*/
public function execute(/* array */ $params = null): bool
public function execute(array $params = null): bool
{
if (
\is_array($params)
@ -96,10 +103,20 @@ class DBStatement extends PDOStatement
) {
$this->bindValueList($params);
}
$start = \microtime(true);
$result = parent::execute();
$this->db->saveQuery($this->queryString, \microtime(true) - $start);
$result = $this->stmt->execute();
$this->db->saveQuery($this->stmt->queryString, \microtime(true) - $start);
return $result;
}
/**
* Передает вызовы метода в PDOStatement
*/
public function __call(string $name, array $args) /* : mixed */
{
return $this->stmt->$name(...$args);
}
}

View file

@ -10,26 +10,79 @@ declare(strict_types=1);
namespace ForkBB\Core\DB;
use ForkBB\Core\DB\AbstractSqliteStatement;
use ForkBB\Core\DB\AbstractStatement;
use PDO;
/**
* For PHP 8
*/
class SqliteStatement extends AbstractSqliteStatement
class SqliteStatement extends AbstractStatement
{
public function fetch(int $mode = 0 /* PDO::FETCH_DEFAULT */, int $orientation = PDO::FETCH_ORI_NEXT, int $offset = 0): mixed
/**
* https://github.com/php/php-src/blob/master/ext/pdo_sqlite/sqlite_statement.c
*
* SQLite:
* native_type:
* null - для значения NULL, а не типа столбца
* integer - это INTEGER, NUMERIC(?), BOOLEAN // BOOLEAN тут как-то не к месту, его бы в отдельный тип
* string - это TEXT
* double - это REAL, NUMERIC(?) // NUMERIC может быть и double, и integer
* sqlite:decl_type:
* INTEGER
* TEXT
* REAL
* NUMERIC
* BOOLEAN
* ... (это те типы, которые прописаны в CREATE TABLE и полученные после перекодировки из {driver}::bTypeRepl)
*/
/**
* @var array
*/
protected $nativeTypeRepl = [
'integer' => self::INTEGER,
'double' => self::FLOAT,
];
public function getColumnsType(): array
{
return $this->dbFetch($mode, $orientation, $offset);
if (isset($this->columnsType)) {
return $this->columnsType;
}
$this->columnsType = [];
$count = $this->columnCount();
$i = 0;
// $dbType = $this->db->getType();
for ($i = 0; $i < $count; $i++) {
$meta = $this->getColumnMeta($i);
$type = null;
// $declType = $meta[$dbType . ':decl_type'] ?? null;
$declType = $meta['sqlite:decl_type'] ?? null;
if (null === $declType) {
$type = $this->nativeTypeRepl[$meta['native_type']] ?? null;
} elseif (\preg_match('%INT%i', $declType)) {
$type = self::INTEGER;
} elseif (\preg_match('%BOOL%i', $declType)) {
$type = self::BOOLEAN;
// } elseif (\preg_match('%REAL|FLOA|DOUB|NUMERIC|DECIMAL%i', $declType)) {
// $type = self::FLOAT;
}
if ($type) {
$this->columnsType[$i] = $type;
if (isset($meta['name'])) { // ????? проверка на тип содержимого? только строки, не числа?
$this->columnsType[$meta['name']] = $type;
}
}
}
return $this->columnsType;
}
public function fetchAll(int $mode = 0 /* PDO::FETCH_DEFAULT */, ...$args): array
protected function convToBoolean(/* mixed */ $value): bool
{
return $this->dbFetchAll($mode, ...$args);
}
public function setFetchMode(int $mode, ...$args): bool
{
return $this->dbSetFetchMode($mode, ...$args);
return (bool) $value;
}
}

View file

@ -1,59 +0,0 @@
<?php
/**
* This file is part of the ForkBB <https://github.com/forkbb>.
*
* @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
* @license The MIT License (MIT)
*/
declare(strict_types=1);
namespace ForkBB\Core\DB;
use ForkBB\Core\DB\AbstractSqliteStatement;
use PDO;
/**
* For PHP 7
*/
class SqliteStatement7 extends AbstractSqliteStatement
{
public function fetch($mode = null, $orientation = null, $offset = null)
{
$mode = $mode ?? 0;
$orientation = $orientation ?? PDO::FETCH_ORI_NEXT;
$offset = $offset ?? 0;
return $this->dbFetch($mode, $orientation, $offset);
}
public function fetchAll($mode = null, $fetchArg = null, $ctorArgs = null)
{
$mode = $mode ?? 0;
$args = $this->returnArgs($fetchArg, $ctorArgs);
return $this->dbFetchAll($mode, ...$args);
}
public function setFetchMode($mode, $fetchArg = null, $ctorArgs = null): bool
{
$args = $this->returnArgs($fetchArg, $ctorArgs);
return $this->dbSetFetchMode($mode, ...$args);
}
protected function returnArgs($fetchArg, $ctorArgs): array
{
$args = [];
if (isset($fetchArg)) {
$args[] = $fetchArg;
if (isset($ctorArgs)) {
$args[] = $ctorArgs;
}
}
return $args;
}
}

View file

@ -506,12 +506,17 @@ class Install extends Admin
break;
case 'sqlite':
$this->c->DB_DSN = "sqlite:!PATH!{$dbname}";
$this->c->DB_OPTS_AS_STR = '\\PDO::ATTR_TIMEOUT => 5, /* \'initSQLCommands\' => [\'PRAGMA journal_mode=WAL\',], */';
$this->c->DB_OPTS_AS_STR = "\n"
. ' \\PDO::ATTR_TIMEOUT => 5,' . "\n"
. ' /* \'initSQLCommands\' => [\'PRAGMA journal_mode=WAL\',], */' . "\n"
. ' \'initFunction\' => function ($db) {return $db->sqliteCreateFunction(\'CONCAT\', function (...$args) {return \\implode(\'\', $args);});},' . "\n"
. ' ';
$this->c->DB_OPTIONS = [
PDO::ATTR_TIMEOUT => 5,
'initSQLCommands' => [
'PRAGMA journal_mode=WAL',
],
'initFunction' => function ($db) {return $db->sqliteCreateFunction('CONCAT', function (...$args) {return \implode('', $args);});},
];
break;

View file

@ -179,7 +179,7 @@ class Delete extends Action
}
if ($uidsUpdate) {
$this->c->users->UpdateCountTopics(...$uidsUpdate);
$this->c->users->updateCountTopics(...$uidsUpdate);
}
}
}

View file

@ -54,26 +54,19 @@ class Merge extends Action
}
//???? перенести обработку в посты?
$vars = [
'start' => "[from]",
'end' => "[/from]\n",
'topics' => $ids,
];
$query = 'UPDATE ::posts AS p, ::topics as t
SET p.message=CONCAT(?s:start, t.subject, ?s:end, p.message)
WHERE p.topic_id IN (?ai:topics) AND t.id=p.topic_id';
$query = 'UPDATE ::posts
SET message=CONCAT(?s:prefix, message), topic_id=?i:new
WHERE topic_id=?i:id';
$this->c->DB->exec($query, $vars);
foreach ($otherTopics as $topic) {
$vars = [
':new' => $firstTopic->id,
':id' => $topic->id,
':prefix' => "[from]{$topic->subject}[/from]\n",
];
$vars = [
'id' => $firstTopic->id,
'topics' => $ids,
];
$query = 'UPDATE ::posts AS p
SET p.topic_id=?i:id
WHERE p.topic_id IN (?ai:topics)';
$this->c->DB->exec($query, $vars);
$this->c->DB->exec($query, $vars);
}
// добавить перенос подписок на первую тему?
@ -104,7 +97,7 @@ class Merge extends Action
}
if ($users) {
$this->c->users->UpdateCountTopics(...$users);
$this->c->users->updateCountTopics(...$users);
}
} else {
$this->c->topics->update($firstTopic->calcStat());

View file

@ -71,9 +71,9 @@ class ChangeGroup extends Action
':new' => $newGroupId,
':ids' => $ids,
];
$query = 'UPDATE ::users AS u
SET u.group_id = ?i:new
WHERE u.id IN (?ai:ids)';
$query = 'UPDATE ::users
SET group_id = ?i:new
WHERE id IN (?ai:ids)';
$this->c->DB->exec($query, $vars);

View file

@ -38,22 +38,22 @@ class UpdateCountPosts extends Action
unset($ids[0]); // ????
if (empty($ids)) {
$where = 'u.id > 0';
$where = '::users.id > 0';
$vars = [];
} else {
$where = 'u.id IN (?ai:ids)';
$where = '::users.id IN (?ai:ids)';
$vars = [
':ids' => \array_keys($ids),
];
}
$query = 'UPDATE ::users AS u
SET u.num_posts = COALESCE((
$query = 'UPDATE ::users
SET num_posts = COALESCE((
SELECT COUNT(p.id)
FROM ::posts AS p
INNER JOIN ::topics AS t ON t.id=p.topic_id
INNER JOIN ::forums AS f ON f.id=t.forum_id
WHERE p.poster_id=u.id AND f.no_sum_mess=0
WHERE p.poster_id=::users.id AND f.no_sum_mess=0
GROUP BY p.poster_id
), 0)
WHERE ' . $where;

View file

@ -38,21 +38,21 @@ class UpdateCountTopics extends Action
unset($ids[0]); // ????
if (empty($ids)) {
$where = 'u.id > 0';
$where = '::users.id > 0';
$vars = [];
} else {
$where = 'u.id IN (?ai:ids)';
$where = '::users.id IN (?ai:ids)';
$vars = [
':ids' => \array_keys($ids),
];
}
$query = 'UPDATE ::users AS u
SET u.num_topics = COALESCE((
$query = 'UPDATE ::users
SET num_topics = COALESCE((
SELECT COUNT(t.id)
FROM ::topics AS t
INNER JOIN ::posts AS p ON t.first_post_id=p.id
WHERE p.poster_id=u.id AND t.moved_to=0
WHERE p.poster_id=::users.id AND t.moved_to=0
GROUP BY p.poster_id
), 0)
WHERE ' . $where;