Merge pull request #7 from forkbb/add_PostgreSQL
Add PostgreSQL support
This commit is contained in:
commit
bc055d2e40
11 changed files with 729 additions and 109 deletions
|
@ -74,8 +74,12 @@ class DB extends PDO
|
|||
self::ATTR_STATEMENT_CLASS => [DBStatement::class, [$this]],
|
||||
];
|
||||
|
||||
$start = \microtime(true);
|
||||
|
||||
parent::__construct($dsn, $username, $password, $options);
|
||||
|
||||
$this->saveQuery('PDO::__construct()', \microtime(true) - $start, false);
|
||||
|
||||
$this->beginTransaction();
|
||||
}
|
||||
|
||||
|
|
|
@ -97,9 +97,10 @@ class Mysql
|
|||
|
||||
$value = "`{$value}`";
|
||||
}
|
||||
unset($value);
|
||||
}
|
||||
|
||||
unset($value);
|
||||
|
||||
return \implode(',', $arr);
|
||||
}
|
||||
|
||||
|
@ -132,22 +133,17 @@ class Mysql
|
|||
*/
|
||||
public function tableExists(string $table, bool $noPrefix = false): bool
|
||||
{
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
$vars = [
|
||||
':tname' => ($noPrefix ? '' : $this->dbPrefix) . $table,
|
||||
];
|
||||
$query = 'SELECT 1
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?s:tname';
|
||||
|
||||
try {
|
||||
$vars = [
|
||||
':table' => $table,
|
||||
];
|
||||
$query = 'SELECT 1
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?s:table';
|
||||
$stmt = $this->db->query($query, $vars);
|
||||
$result = $stmt->fetch();
|
||||
|
||||
$stmt = $this->db->query($query, $vars);
|
||||
$result = $stmt->fetch();
|
||||
$stmt->closeCursor();
|
||||
} catch (PDOException $e) {
|
||||
return false;
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
|
||||
return ! empty($result);
|
||||
}
|
||||
|
@ -157,23 +153,18 @@ class Mysql
|
|||
*/
|
||||
public function fieldExists(string $table, string $field, bool $noPrefix = false): bool
|
||||
{
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
$vars = [
|
||||
':tname' => ($noPrefix ? '' : $this->dbPrefix) . $table,
|
||||
':fname' => $field,
|
||||
];
|
||||
$query = 'SELECT 1
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?s:tname AND COLUMN_NAME = ?s:fname';
|
||||
|
||||
try {
|
||||
$vars = [
|
||||
':table' => $table,
|
||||
':field' => $field,
|
||||
];
|
||||
$query = 'SELECT 1
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?s:table AND COLUMN_NAME = ?s:field';
|
||||
$stmt = $this->db->query($query, $vars);
|
||||
$result = $stmt->fetch();
|
||||
|
||||
$stmt = $this->db->query($query, $vars);
|
||||
$result = $stmt->fetch();
|
||||
$stmt->closeCursor();
|
||||
} catch (PDOException $e) {
|
||||
return false;
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
|
||||
return ! empty($result);
|
||||
}
|
||||
|
@ -186,21 +177,18 @@ class Mysql
|
|||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
$index = 'PRIMARY' == $index ? $index : $table . '_' . $index;
|
||||
|
||||
try {
|
||||
$vars = [
|
||||
':table' => $table,
|
||||
':index' => $index,
|
||||
];
|
||||
$query = 'SELECT 1
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?s:table AND INDEX_NAME = ?s:index';
|
||||
$vars = [
|
||||
':tname' => $table,
|
||||
':index' => $index,
|
||||
];
|
||||
$query = 'SELECT 1
|
||||
FROM INFORMATION_SCHEMA.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?s:tname AND INDEX_NAME = ?s:index';
|
||||
|
||||
$stmt = $this->db->query($query, $vars);
|
||||
$result = $stmt->fetch();
|
||||
$stmt->closeCursor();
|
||||
} catch (PDOException $e) {
|
||||
return false;
|
||||
}
|
||||
$stmt = $this->db->query($query, $vars);
|
||||
$result = $stmt->fetch();
|
||||
|
||||
$stmt->closeCursor();
|
||||
|
||||
return ! empty($result);
|
||||
}
|
||||
|
@ -213,8 +201,8 @@ class Mysql
|
|||
$this->testStr($table);
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
|
||||
$query = "CREATE TABLE IF NOT EXISTS `{$table}` (";
|
||||
|
||||
foreach ($schema['FIELDS'] as $field => $data) {
|
||||
$this->testStr($field);
|
||||
// имя и тип
|
||||
|
@ -222,11 +210,13 @@ class Mysql
|
|||
// сравнение
|
||||
if (\preg_match('%^(?:CHAR|VARCHAR|TINYTEXT|TEXT|MEDIUMTEXT|LONGTEXT|ENUM|SET)%i', $data[0])) {
|
||||
$query .= ' CHARACTER SET utf8mb4 COLLATE utf8mb4_';
|
||||
|
||||
if (
|
||||
isset($data[3])
|
||||
&& \is_string($data[3])
|
||||
) {
|
||||
$this->testStr($data[3]);
|
||||
|
||||
$query .= $data[3];
|
||||
} else {
|
||||
$query .= 'unicode_ci';
|
||||
|
@ -240,11 +230,14 @@ class Mysql
|
|||
if (isset($data[2])) {
|
||||
$query .= ' DEFAULT ' . $this->convToStr($data[2]);
|
||||
}
|
||||
|
||||
$query .= ', ';
|
||||
}
|
||||
|
||||
if (isset($schema['PRIMARY KEY'])) {
|
||||
$query .= 'PRIMARY KEY (' . $this->replIdxs($schema['PRIMARY KEY']) . '), ';
|
||||
}
|
||||
|
||||
if (isset($schema['UNIQUE KEYS'])) {
|
||||
foreach ($schema['UNIQUE KEYS'] as $key => $fields) {
|
||||
$this->testStr($key);
|
||||
|
@ -252,6 +245,7 @@ class Mysql
|
|||
$query .= "UNIQUE `{$table}_{$key}` (" . $this->replIdxs($fields) . '), ';
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($schema['INDEXES'])) {
|
||||
foreach ($schema['INDEXES'] as $index => $fields) {
|
||||
$this->testStr($index);
|
||||
|
@ -259,13 +253,15 @@ class Mysql
|
|||
$query .= "INDEX `{$table}_{$index}` (" . $this->replIdxs($fields) . '), ';
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($schema['ENGINE'])) {
|
||||
$engine = $schema['ENGINE'];
|
||||
} else {
|
||||
// при отсутствии типа таблицы он определяется на основании типов других таблиц в базе
|
||||
$prefix = \str_replace('_', '\\_', $this->dbPrefix);
|
||||
$stmt = $this->db->query("SHOW TABLE STATUS LIKE '{$prefix}%'");
|
||||
$stmt = $this->db->query("SHOW TABLE STATUS LIKE '{$prefix}%'");
|
||||
$engine = [];
|
||||
|
||||
while ($row = $stmt->fetch()) {
|
||||
if (isset($engine[$row['Engine']])) {
|
||||
++$engine[$row['Engine']];
|
||||
|
@ -283,7 +279,9 @@ class Mysql
|
|||
$engine = \array_shift($engine);
|
||||
}
|
||||
}
|
||||
|
||||
$this->testStr($engine);
|
||||
|
||||
$query = \rtrim($query, ', ') . ") ENGINE={$engine} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci";
|
||||
|
||||
return false !== $this->db->exec($query);
|
||||
|
@ -335,14 +333,16 @@ class Mysql
|
|||
}
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
|
||||
$query = "ALTER TABLE `{$table}` ADD `{$field}` " . $this->replType($type);
|
||||
|
||||
if (! $allowNull) {
|
||||
$query .= ' NOT NULL';
|
||||
}
|
||||
|
||||
if (null !== $default) {
|
||||
$query .= ' DEFAULT ' . $this->convToStr($default);
|
||||
}
|
||||
|
||||
if (null !== $after) {
|
||||
$this->testStr($after);
|
||||
|
||||
|
@ -361,14 +361,16 @@ class Mysql
|
|||
$this->testStr($field);
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
|
||||
$query = "ALTER TABLE `{$table}` MODIFY `{$field}` " . $this->replType($type);
|
||||
|
||||
if (! $allowNull) {
|
||||
$query .= ' NOT NULL';
|
||||
}
|
||||
|
||||
if (null !== $default) {
|
||||
$query .= ' DEFAULT ' . $this->convToStr($default);
|
||||
}
|
||||
|
||||
if (null !== $after) {
|
||||
$this->testStr($after);
|
||||
|
||||
|
@ -405,38 +407,37 @@ class Mysql
|
|||
$this->testStr($new);
|
||||
|
||||
if (
|
||||
! $this->fieldExists($table, $old, $noPrefix)
|
||||
|| $this->fieldExists($table, $new, $noPrefix)
|
||||
$this->fieldExists($table, $new, $noPrefix)
|
||||
&& ! $this->fieldExists($table, $old, $noPrefix)
|
||||
) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
|
||||
try {
|
||||
$vars = [
|
||||
':table' => $table,
|
||||
':field' => $old,
|
||||
];
|
||||
$query = 'SELECT COLUMN_DEFAULT, IS_NULLABLE, COLUMN_TYPE
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?s:table AND COLUMN_NAME = ?s:field';
|
||||
$vars = [
|
||||
':tname' => $table,
|
||||
':fname' => $old,
|
||||
];
|
||||
$query = 'SELECT COLUMN_DEFAULT, IS_NULLABLE, COLUMN_TYPE
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?s:tname AND COLUMN_NAME = ?s:fname';
|
||||
|
||||
$stmt = $this->db->query($query, $vars);
|
||||
$result = $stmt->fetch();
|
||||
$stmt->closeCursor();
|
||||
$stmt = $this->db->query($query, $vars);
|
||||
$result = $stmt->fetch();
|
||||
|
||||
$type = $result['COLUMN_TYPE'];
|
||||
$allowNull = 'YES' == $result['IS_NULLABLE'];
|
||||
$default = $result['COLUMN_DEFAULT'];
|
||||
} catch (PDOException $e) {
|
||||
return false;
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
|
||||
$type = $result['COLUMN_TYPE'];
|
||||
$allowNull = 'YES' == $result['IS_NULLABLE'];
|
||||
$default = $result['COLUMN_DEFAULT'];
|
||||
|
||||
$query = "ALTER TABLE `{$table}` CHANGE COLUMN `{$old}` `{$new}` " . $this->replType($type);
|
||||
|
||||
if (! $allowNull) {
|
||||
$query .= ' NOT NULL';
|
||||
}
|
||||
|
||||
if (null !== $default) {
|
||||
$query .= ' DEFAULT ' . $this->convToStr($default);
|
||||
}
|
||||
|
@ -456,8 +457,8 @@ class Mysql
|
|||
}
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
|
||||
$query = "ALTER TABLE `{$table}` ADD ";
|
||||
|
||||
if ('PRIMARY' == $index) {
|
||||
$query .= 'PRIMARY KEY';
|
||||
} else {
|
||||
|
@ -471,6 +472,7 @@ class Mysql
|
|||
$query .= "INDEX `{$index}`";
|
||||
}
|
||||
}
|
||||
|
||||
$query .= ' (' . $this->replIdxs($fields) . ')';
|
||||
|
||||
return false !== $this->db->exec($query);
|
||||
|
@ -488,8 +490,8 @@ class Mysql
|
|||
}
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
|
||||
$query = "ALTER TABLE `{$table}` ";
|
||||
|
||||
if ('PRIMARY' == $index) {
|
||||
$query .= "DROP PRIMARY KEY";
|
||||
} else {
|
||||
|
@ -551,7 +553,8 @@ class Mysql
|
|||
}
|
||||
|
||||
return [
|
||||
'db' => 'MySQL (PDO) ' . $this->db->getAttribute(PDO::ATTR_SERVER_VERSION) . ' : ' . implode(', ', $tmp),
|
||||
'db' => 'MySQL (PDO) v.' . $this->db->getAttribute(PDO::ATTR_SERVER_VERSION),
|
||||
'tables' => implode(', ', $tmp),
|
||||
'records' => $records,
|
||||
'size' => $size,
|
||||
'server info' => $this->db->getAttribute(PDO::ATTR_SERVER_INFO),
|
||||
|
@ -564,7 +567,7 @@ class Mysql
|
|||
public function getMap(): array
|
||||
{
|
||||
$vars = [
|
||||
"{$this->dbPrefix}%",
|
||||
str_replace('_', '\\_', $this->dbPrefix) . '%',
|
||||
];
|
||||
$query = 'SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
|
@ -573,12 +576,14 @@ class Mysql
|
|||
$stmt = $this->db->query($query, $vars);
|
||||
$result = [];
|
||||
$table = null;
|
||||
|
||||
while ($row = $stmt->fetch()) {
|
||||
if ($table !== $row['TABLE_NAME']) {
|
||||
$table = $row['TABLE_NAME'];
|
||||
$tableNoPref = \substr($table, \strlen($this->dbPrefix));
|
||||
$table = $row['TABLE_NAME'];
|
||||
$tableNoPref = \substr($table, \strlen($this->dbPrefix));
|
||||
$result[$tableNoPref] = [];
|
||||
}
|
||||
|
||||
$type = \strtolower($row['DATA_TYPE']);
|
||||
$result[$tableNoPref][$row['COLUMN_NAME']] = $this->types[$type] ?? 's';
|
||||
}
|
||||
|
|
547
app/Core/DB/Pgsql.php
Normal file
547
app/Core/DB/Pgsql.php
Normal file
|
@ -0,0 +1,547 @@
|
|||
<?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;
|
||||
use PDO;
|
||||
use PDOStatement;
|
||||
use PDOException;
|
||||
|
||||
class Pgsql
|
||||
{
|
||||
/**
|
||||
* @var DB
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* Префикс для таблиц базы
|
||||
* @var string
|
||||
*/
|
||||
protected $dbPrefix;
|
||||
|
||||
/**
|
||||
* Массив замены типов полей таблицы
|
||||
* @var array
|
||||
*/
|
||||
protected $dbTypeRepl = [
|
||||
'%^(?:TINY|SMALL)INT(?:\s*\(\d+\))?(?:\s*UNSIGNED)?$%i' => 'SMALLINT',
|
||||
'%^(?:MEDIUM)?INT(?:\s*\(\d+\))?(?:\s*UNSIGNED)?$%i' => 'INTEGER',
|
||||
'%^BIGINT(?:\s*\(\d+\))?(?:\s*UNSIGNED)?$%i' => 'BIGINT',
|
||||
'%^(?:TINY|MEDIUM|LONG)TEXT$%i' => 'TEXT',
|
||||
'%^DOUBLE(?:\s+PRECISION)?(?:\s*\([\d,]+\))?(?:\s*UNSIGNED)?$%i' => 'DOUBLE PRECISION',
|
||||
'%^FLOAT(?:\s*\([\d,]+\))?(?:\s*UNSIGNED)?$%i' => 'REAL',
|
||||
];
|
||||
|
||||
/**
|
||||
* Подстановка типов полей для карты БД
|
||||
* @var array
|
||||
*/
|
||||
protected $types = [
|
||||
'bool' => 'b',
|
||||
'boolean' => 'b',
|
||||
'tinyint' => 'i',
|
||||
'smallint' => 'i',
|
||||
'mediumint' => 'i',
|
||||
'int' => 'i',
|
||||
'integer' => 'i',
|
||||
'bigint' => 'i',
|
||||
'decimal' => 'i',
|
||||
'dec' => 'i',
|
||||
'float' => 'i',
|
||||
'real' => 'i',
|
||||
'double' => 'i',
|
||||
'double precision' => 'i',
|
||||
];
|
||||
|
||||
public function __construct(DB $db, string $prefix)
|
||||
{
|
||||
$this->db = $db;
|
||||
|
||||
$this->testStr($prefix);
|
||||
|
||||
$this->dbPrefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Перехват неизвестных методов
|
||||
*/
|
||||
public function __call(string $name, array $args)
|
||||
{
|
||||
throw new PDOException("Method '{$name}' not found in DB driver.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет строку на допустимые символы
|
||||
*/
|
||||
protected function testStr(string $str): void
|
||||
{
|
||||
if (\preg_match('%[^a-zA-Z0-9_]%', $str)) {
|
||||
throw new PDOException("Name '{$str}' have bad characters.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Операции над полями индексов: проверка, замена
|
||||
*/
|
||||
protected function replIdxs(array $arr): string
|
||||
{
|
||||
foreach ($arr as &$value) {
|
||||
if (\preg_match('%^(.*)\s*(\(\d+\))$%', $value, $matches)) {
|
||||
$this->testStr($matches[1]);
|
||||
|
||||
$value = "\"{$matches[1]}\""; // {$matches[2]}
|
||||
} else {
|
||||
$this->testStr($value);
|
||||
|
||||
$value = "\"{$value}\"";
|
||||
}
|
||||
}
|
||||
|
||||
unset($value);
|
||||
|
||||
return \implode(',', $arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Замена типа поля в соответствии с dbTypeRepl
|
||||
*/
|
||||
protected function replType(string $type): string
|
||||
{
|
||||
return \preg_replace(\array_keys($this->dbTypeRepl), \array_values($this->dbTypeRepl), $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Конвертирует данные в строку для DEFAULT
|
||||
*/
|
||||
protected function convToStr(/* mixed */ $data): string
|
||||
{
|
||||
if (\is_string($data)) {
|
||||
return $this->db->quote($data);
|
||||
} elseif (\is_numeric($data)) {
|
||||
return (string) $data;
|
||||
} elseif (\is_bool($data)) {
|
||||
return $data ? 'true' : 'false';
|
||||
} else {
|
||||
throw new PDOException('Invalid data type for DEFAULT.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет наличие таблицы в базе
|
||||
*/
|
||||
public function tableExists(string $table, bool $noPrefix = false): bool
|
||||
{
|
||||
$vars = [
|
||||
':schema' => 'public',
|
||||
':tname' => ($noPrefix ? '' : $this->dbPrefix) . $table,
|
||||
':ttype' => 'r',
|
||||
];
|
||||
$query = 'SELECT 1
|
||||
FROM pg_class AS c
|
||||
INNER JOIN pg_namespace AS n ON n.oid=c.relnamespace
|
||||
WHERE c.relname=?s:tname AND c.relkind=?s:ttype AND n.nspname=?s:schema';
|
||||
|
||||
$stmt = $this->db->query($query, $vars);
|
||||
$result = $stmt->fetch();
|
||||
|
||||
$stmt->closeCursor();
|
||||
|
||||
return ! empty($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет наличие поля в таблице
|
||||
*/
|
||||
public function fieldExists(string $table, string $field, bool $noPrefix = false): bool
|
||||
{
|
||||
$vars = [
|
||||
':schema' => 'public',
|
||||
':tname' => ($noPrefix ? '' : $this->dbPrefix) . $table,
|
||||
':ttype' => 'r',
|
||||
':fname' => $field,
|
||||
];
|
||||
$query = 'SELECT 1
|
||||
FROM pg_attribute AS a
|
||||
INNER JOIN pg_class AS c ON a.attrelid=c.oid
|
||||
INNER JOIN pg_namespace AS n ON n.oid=c.relnamespace
|
||||
WHERE a.attname=?s:fname AND c.relname=?s:tname AND c.relkind=?s:ttype AND n.nspname=?s:schema';
|
||||
|
||||
$stmt = $this->db->query($query, $vars);
|
||||
$result = $stmt->fetch();
|
||||
|
||||
$stmt->closeCursor();
|
||||
|
||||
return ! empty($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет наличие индекса в таблице
|
||||
*/
|
||||
public function indexExists(string $table, string $index, bool $noPrefix = false): bool
|
||||
{
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
$index = $table . '_' . ('PRIMARY' === $index ? 'pkey' : $index);
|
||||
|
||||
$vars = [
|
||||
':schema' => 'public',
|
||||
':tname' => $table,
|
||||
':ttype' => 'r',
|
||||
':iname' => $index,
|
||||
':itype' => 'i',
|
||||
];
|
||||
$query = 'SELECT 1
|
||||
FROM pg_class AS i
|
||||
INNER JOIN pg_index AS ix ON ix.indexrelid=i.oid
|
||||
INNER JOIN pg_class AS c ON c.oid=ix.indrelid
|
||||
INNER JOIN pg_namespace AS n ON n.oid=c.relnamespace
|
||||
WHERE i.relname=?s:iname
|
||||
AND i.relkind=?s:itype
|
||||
AND c.relname=?s:tname
|
||||
AND c.relkind=?s:ttype
|
||||
AND n.nspname=?s:schema';
|
||||
|
||||
$stmt = $this->db->query($query, $vars);
|
||||
$result = $stmt->fetch();
|
||||
|
||||
$stmt->closeCursor();
|
||||
|
||||
return ! empty($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает таблицу
|
||||
*/
|
||||
public function createTable(string $table, array $schema, bool $noPrefix = false): bool
|
||||
{
|
||||
$this->testStr($table);
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
$query = "CREATE TABLE IF NOT EXISTS \"{$table}\" (";
|
||||
|
||||
foreach ($schema['FIELDS'] as $field => $data) {
|
||||
$this->testStr($field);
|
||||
// имя и тип
|
||||
$query .= "\"{$field}\" " . $this->replType($data[0]);
|
||||
|
||||
if ('SERIAL' !== \strtoupper($data[0])) {
|
||||
// не NULL
|
||||
if (empty($data[1])) {
|
||||
$query .= ' NOT NULL';
|
||||
}
|
||||
// значение по умолчанию
|
||||
if (isset($data[2])) {
|
||||
$query .= ' DEFAULT ' . $this->convToStr($data[2]);
|
||||
}
|
||||
}
|
||||
|
||||
$query .= ', ';
|
||||
}
|
||||
|
||||
if (isset($schema['PRIMARY KEY'])) {
|
||||
$query .= 'PRIMARY KEY (' . $this->replIdxs($schema['PRIMARY KEY']) . '), ';
|
||||
}
|
||||
|
||||
$query = \rtrim($query, ', ') . ")";
|
||||
$result = false !== $this->db->exec($query);
|
||||
|
||||
// вынесено отдельно для сохранения имен индексов
|
||||
if ($result && isset($schema['UNIQUE KEYS'])) {
|
||||
foreach ($schema['UNIQUE KEYS'] as $key => $fields) {
|
||||
$result = $result && $this->addIndex($table, $key, $fields, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
if ($result && isset($schema['INDEXES'])) {
|
||||
foreach ($schema['INDEXES'] as $index => $fields) {
|
||||
$result = $result && $this->addIndex($table, $index, $fields, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляет таблицу
|
||||
*/
|
||||
public function dropTable(string $table, bool $noPrefix = false): bool
|
||||
{
|
||||
$this->testStr($table);
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
|
||||
return false !== $this->db->exec("DROP TABLE IF EXISTS \"{$table}\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Переименовывает таблицу
|
||||
*/
|
||||
public function renameTable(string $old, string $new, bool $noPrefix = false): bool
|
||||
{
|
||||
$this->testStr($old);
|
||||
$this->testStr($new);
|
||||
|
||||
if (
|
||||
$this->tableExists($new, $noPrefix)
|
||||
&& ! $this->tableExists($old, $noPrefix)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$old = ($noPrefix ? '' : $this->dbPrefix) . $old;
|
||||
$new = ($noPrefix ? '' : $this->dbPrefix) . $new;
|
||||
|
||||
return false !== $this->db->exec("ALTER TABLE \"{$old}\" RENAME TO \"{$new}\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет поле в таблицу
|
||||
*/
|
||||
public function addField(string $table, string $field, string $type, bool $allowNull, /* mixed */ $default = null, string $after = null, bool $noPrefix = false): bool
|
||||
{
|
||||
$this->testStr($table);
|
||||
$this->testStr($field);
|
||||
|
||||
if ($this->fieldExists($table, $field, $noPrefix)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
$query = "ALTER TABLE \"{$table}\" ADD \"{$field}\" " . $this->replType($type);
|
||||
|
||||
if ('SERIAL' !== \strtoupper($type)) {
|
||||
if (! $allowNull) {
|
||||
$query .= ' NOT NULL';
|
||||
}
|
||||
|
||||
if (null !== $default) {
|
||||
$query .= ' DEFAULT ' . $this->convToStr($default);
|
||||
}
|
||||
}
|
||||
|
||||
return false !== $this->db->exec($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Модифицирует поле в таблице
|
||||
*/
|
||||
public function alterField(string $table, string $field, string $type, bool $allowNull, /* mixed */ $default = null, string $after = null, bool $noPrefix = false): bool
|
||||
{
|
||||
$this->testStr($table);
|
||||
$this->testStr($field);
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
$query = "ALTER TABLE \"{$table}\"";
|
||||
|
||||
if ('SERIAL' === \strtoupper($type) || $allowNull) {
|
||||
$query .= " ALTER COLUMN \"{$field}\" DROP NOT NULL,";
|
||||
}
|
||||
|
||||
if ('SERIAL' === \strtoupper($type) || null === $default) {
|
||||
$query .= " ALTER COLUMN \"{$field}\" DROP DEFAULT,";
|
||||
}
|
||||
|
||||
$query = " ALTER COLUMN \"{$field}\" TYPE " . $this->replType($type) . ','; // ???? Использовать USING?
|
||||
|
||||
if ('SERIAL' !== \strtoupper($type) && ! $allowNull) {
|
||||
$query .= " ALTER COLUMN \"{$field}\" SET NOT NULL,";
|
||||
}
|
||||
|
||||
if ('SERIAL' !== \strtoupper($type) && null !== $default) {
|
||||
$query .= " ALTER COLUMN \"{$field}\" SET DEFAULT " . $this->convToStr($default) . ',';
|
||||
}
|
||||
|
||||
$query = \rtrim($query, ',');
|
||||
|
||||
return false !== $this->db->exec($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляет поле из таблицы
|
||||
*/
|
||||
public function dropField(string $table, string $field, bool $noPrefix = false): bool
|
||||
{
|
||||
$this->testStr($table);
|
||||
$this->testStr($field);
|
||||
|
||||
if (! $this->fieldExists($table, $field, $noPrefix)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
|
||||
return false !== $this->db->exec("ALTER TABLE \"{$table}\" DROP COLUMN \"{$field}\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Переименование поля в таблице
|
||||
*/
|
||||
public function renameField(string $table, string $old, string $new, bool $noPrefix = false): bool
|
||||
{
|
||||
$this->testStr($table);
|
||||
$this->testStr($old);
|
||||
$this->testStr($new);
|
||||
|
||||
if (
|
||||
$this->fieldExists($table, $new, $noPrefix)
|
||||
&& ! $this->fieldExists($table, $old, $noPrefix)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
|
||||
return false !== $this->db->exec("ALTER TABLE \"{$table}\" RENAME COLUMN \"{$old}\" TO \"{$new}\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет индекс в таблицу
|
||||
*/
|
||||
public function addIndex(string $table, string $index, array $fields, bool $unique = false, bool $noPrefix = false): bool
|
||||
{
|
||||
$this->testStr($table);
|
||||
|
||||
if ($this->indexExists($table, $index, $noPrefix)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
|
||||
if ('PRIMARY' === $index) {
|
||||
// ?????
|
||||
} else {
|
||||
$index = $table . '_' . $index;
|
||||
|
||||
$this->testStr($index);
|
||||
|
||||
$unique = $unique ? 'UNIQUE' : '';
|
||||
$query = "CREATE {$unique} INDEX \"{$index}\" ON \"{$table}\" (" . $this->replIdxs($fields) . ')';
|
||||
}
|
||||
|
||||
return false !== $this->db->exec($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляет индекс из таблицы
|
||||
*/
|
||||
public function dropIndex(string $table, string $index, bool $noPrefix = false): bool
|
||||
{
|
||||
$this->testStr($table);
|
||||
|
||||
if (! $this->indexExists($table, $index, $noPrefix)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
$index = $table . '_' . ('PRIMARY' === $index ? 'pkey' : $index);
|
||||
|
||||
$this->testStr($index);
|
||||
|
||||
return false !== $this->db->exec("DROP INDEX \"{$index}\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает таблицу
|
||||
*/
|
||||
public function truncateTable(string $table, bool $noPrefix = false): bool
|
||||
{
|
||||
$this->testStr($table);
|
||||
|
||||
$table = ($noPrefix ? '' : $this->dbPrefix) . $table;
|
||||
|
||||
return false !== $this->db->exec("TRUNCATE TABLE ONLY \"{$table}\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает статистику
|
||||
*/
|
||||
public function statistics(): array
|
||||
{
|
||||
$records = $size = $tables = 0;
|
||||
|
||||
$vars = [
|
||||
':schema' => 'public',
|
||||
':tname' => str_replace('_', '\\_', $this->dbPrefix) . '%',
|
||||
];
|
||||
$query = 'SELECT c.relname, c.relpages, c.reltuples, c.relkind
|
||||
FROM pg_class AS c
|
||||
INNER JOIN pg_namespace AS n ON n.oid=c.relnamespace
|
||||
WHERE n.nspname=?s:schema AND c.relname LIKE ?s:tname';
|
||||
|
||||
$stmt = $this->db->query($query, $vars);
|
||||
|
||||
while ($row = $stmt->fetch()) {
|
||||
if ('r' === $row['relkind']) { // обычная таблица
|
||||
++$tables;
|
||||
$records += $row['reltuples'];
|
||||
}
|
||||
|
||||
$size += $row['relpages'];
|
||||
}
|
||||
|
||||
$other = [
|
||||
'pg_database_size' => $this->db->query('SELECT pg_size_pretty(pg_database_size(current_database()))')->fetchColumn(),
|
||||
]
|
||||
+ $this->db->query('SELECT name, setting FROM pg_settings WHERE category ~ \'Locale\'')->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||
/*
|
||||
$stmt = $this->db->query('SHOW ALL');
|
||||
|
||||
while ($row = $stmt->fetch()) {
|
||||
if ('block_size' === $row['name']) {
|
||||
$blockSize = (int) $row['setting'];
|
||||
} elseif (\preg_match('%^lc_|_encoding%', $row['name'])) {
|
||||
$other[$row['name']] = $row['setting'];
|
||||
}
|
||||
}
|
||||
*/
|
||||
$blockSize = $this->db->query('SELECT current_setting(\'block_size\')')->fetchColumn();
|
||||
$size *= $blockSize ?: 8192;
|
||||
|
||||
return [
|
||||
'db' => 'PostgreSQL (PDO) v.' . $this->db->getAttribute(PDO::ATTR_SERVER_VERSION),
|
||||
'tables' => (string) $tables,
|
||||
'records' => $records,
|
||||
'size' => $size,
|
||||
// 'server info' => $this->db->getAttribute(PDO::ATTR_SERVER_INFO),
|
||||
] + $other;
|
||||
}
|
||||
|
||||
/**
|
||||
* Формирует карту базы данных
|
||||
*/
|
||||
public function getMap(): array
|
||||
{
|
||||
$vars = [
|
||||
':schema' => 'public',
|
||||
':tname' => str_replace('_', '\\_', $this->dbPrefix) . '%',
|
||||
];
|
||||
$query = 'SELECT table_name, column_name, data_type
|
||||
FROM information_schema.columns
|
||||
WHERE table_catalog = current_database() AND table_schema = ?s:schema AND table_name LIKE ?s:tname';
|
||||
|
||||
$stmt = $this->db->query($query, $vars);
|
||||
$result = [];
|
||||
$table = null;
|
||||
|
||||
while ($row = $stmt->fetch()) {
|
||||
if ($table !== $row['table_name']) {
|
||||
$table = $row['table_name'];
|
||||
$tableNoPref = \substr($table, \strlen($this->dbPrefix));
|
||||
$result[$tableNoPref] = [];
|
||||
}
|
||||
|
||||
$type = \strtolower($row['data_type']);
|
||||
$result[$tableNoPref][$row['column_name']] = $this->types[$type] ?? 's';
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -253,8 +253,8 @@ class Groups extends Admin
|
|||
'g_post_links' => 'required|integer|in:0,1',
|
||||
'g_search' => 'required|integer|in:0,1',
|
||||
'g_search_users' => 'required|integer|in:0,1',
|
||||
'g_post_flood' => 'required|integer|min:0|max:999999',
|
||||
'g_search_flood' => 'required|integer|min:0|max:999999',
|
||||
'g_post_flood' => 'required|integer|min:0|max:32767',
|
||||
'g_search_flood' => 'required|integer|min:0|max:32767',
|
||||
]);
|
||||
|
||||
if (
|
||||
|
@ -290,9 +290,9 @@ class Groups extends Admin
|
|||
'g_deledit_interval' => 'required|integer|min:0|max:999999',
|
||||
'g_set_title' => 'required|integer|in:0,1',
|
||||
'g_send_email' => 'required|integer|in:0,1',
|
||||
'g_email_flood' => 'required|integer|min:0|max:999999',
|
||||
'g_report_flood' => 'required|integer|min:0|max:999999',
|
||||
'g_sig_length' => 'required|integer|min:0|max:10000',
|
||||
'g_email_flood' => 'required|integer|min:0|max:32767',
|
||||
'g_report_flood' => 'required|integer|min:0|max:32767',
|
||||
'g_sig_length' => 'required|integer|min:0|max:16000',
|
||||
'g_sig_lines' => 'required|integer|min:0|max:255',
|
||||
'g_pm' => 'required|integer|in:0,1',
|
||||
'g_pm_limit' => 'required|integer|min:0|max:999999',
|
||||
|
@ -654,7 +654,7 @@ class Groups extends Admin
|
|||
$fieldset['g_post_flood'] = [
|
||||
'type' => 'number',
|
||||
'min' => '0',
|
||||
'max' => '999999',
|
||||
'max' => '32767',
|
||||
'value' => $group->g_post_flood,
|
||||
'caption' => 'Post flood label',
|
||||
'help' => 'Post flood help',
|
||||
|
@ -662,7 +662,7 @@ class Groups extends Admin
|
|||
$fieldset['g_search_flood'] = [
|
||||
'type' => 'number',
|
||||
'min' => '0',
|
||||
'max' => '999999',
|
||||
'max' => '32767',
|
||||
'value' => $group->g_search_flood,
|
||||
'caption' => 'Search flood label',
|
||||
'help' => 'Search flood help',
|
||||
|
@ -680,7 +680,7 @@ class Groups extends Admin
|
|||
$fieldset['g_email_flood'] = [
|
||||
'type' => 'number',
|
||||
'min' => '0',
|
||||
'max' => '999999',
|
||||
'max' => '32767',
|
||||
'value' => $group->g_email_flood,
|
||||
'caption' => 'E-mail flood label',
|
||||
'help' => 'E-mail flood help',
|
||||
|
@ -688,7 +688,7 @@ class Groups extends Admin
|
|||
$fieldset['g_report_flood'] = [
|
||||
'type' => 'number',
|
||||
'min' => '0',
|
||||
'max' => '999999',
|
||||
'max' => '32767',
|
||||
'value' => $group->g_report_flood,
|
||||
'caption' => 'Report flood label',
|
||||
'help' => 'Report flood help',
|
||||
|
|
|
@ -457,8 +457,8 @@ class Install extends Admin
|
|||
if (! \preg_match('%^[a-z][a-z\d_]*$%i', $prefix)) {
|
||||
$v->addError('Table prefix error');
|
||||
} elseif (
|
||||
'sqlite' === $v->dbtype
|
||||
&& 'sqlite_' === \strtolower($prefix)
|
||||
'sqlite_' === \strtolower($prefix)
|
||||
|| 'pg_' === \strtolower($prefix)
|
||||
) {
|
||||
$v->addError('Prefix reserved');
|
||||
}
|
||||
|
@ -474,7 +474,7 @@ class Install extends Admin
|
|||
{
|
||||
$this->c->DB_USERNAME = $v->dbuser;
|
||||
$this->c->DB_PASSWORD = $v->dbpass;
|
||||
$this->c->DB_PREFIX = \is_string($v->dbprefix) ? $v->dbprefix : '';
|
||||
$this->c->DB_PREFIX = $v->dbprefix;
|
||||
$dbtype = $v->dbtype;
|
||||
$dbname = $v->dbname;
|
||||
|
||||
|
@ -502,6 +502,16 @@ class Install extends Admin
|
|||
case 'sqlite':
|
||||
break;
|
||||
case 'pgsql':
|
||||
if (\preg_match('%^([^:]+):(\d+)$%', $dbhost, $matches)) {
|
||||
$host = $matches[1];
|
||||
$port = $matches[2];
|
||||
} else {
|
||||
$host = $dbhost;
|
||||
$port = '5432';
|
||||
}
|
||||
|
||||
$this->c->DB_DSN = "pgsql:host={$host} port={$port} dbname={$dbname} options='--client_encoding=UTF8'";
|
||||
|
||||
break;
|
||||
default:
|
||||
//????
|
||||
|
@ -520,16 +530,10 @@ class Install extends Admin
|
|||
}
|
||||
|
||||
// проверка наличия таблицы пользователей в БД
|
||||
try {
|
||||
$stmt = $this->c->DB->query('SELECT 1 FROM ::users LIMIT 1');
|
||||
if ($this->c->DB->tableExists('users')) {
|
||||
$v->addError(['Existing table error', $v->dbprefix, $v->dbname]);
|
||||
|
||||
if (! empty($stmt->fetch())) {
|
||||
$v->addError(['Existing table error', $v->dbprefix, $v->dbname]);
|
||||
|
||||
return $dbhost;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
// все отлично, таблица пользователей не найдена
|
||||
return $dbhost;
|
||||
}
|
||||
|
||||
// база MySQL, кодировка базы отличается от UTF-8 (4 байта)
|
||||
|
@ -540,6 +544,30 @@ class Install extends Admin
|
|||
$v->addError('Bad database charset');
|
||||
}
|
||||
|
||||
// база PostgreSQL, кодировка базы
|
||||
if (
|
||||
isset($stat['server_encoding'])
|
||||
&& 'UTF8' !== $stat['server_encoding']
|
||||
) {
|
||||
$v->addError('Bad database encoding');
|
||||
}
|
||||
|
||||
// база PostgreSQL, порядок сопоставления/сортировки
|
||||
if (
|
||||
isset($stat['lc_collate'])
|
||||
&& 'C' !== $stat['lc_collate']
|
||||
) {
|
||||
$v->addError('Bad database collate');
|
||||
}
|
||||
|
||||
// база PostgreSQL, тип символов
|
||||
if (
|
||||
isset($stat['lc_ctype'])
|
||||
&& 'C' !== $stat['lc_ctype']
|
||||
) {
|
||||
$v->addError('Bad database ctype');
|
||||
}
|
||||
|
||||
return $dbhost;
|
||||
}
|
||||
|
||||
|
@ -1218,17 +1246,29 @@ class Install extends Admin
|
|||
|
||||
|
||||
$ip = \filter_var($_SERVER['REMOTE_ADDR'], \FILTER_VALIDATE_IP) ?: '0.0.0.0';
|
||||
$adminId = 1;
|
||||
$catId = 1;
|
||||
$forumId = 1;
|
||||
$topicId = 1;
|
||||
$postId = 1;
|
||||
|
||||
$this->c->DB->exec('INSERT INTO ::users (id, group_id, username, username_normal, password, email, email_normal, language, style, num_posts, last_post, registered, registration_ip, last_visit, signature, num_topics) VALUES (?i, ?i, ?s, ?s, ?s, ?s, ?s, ?s, ?s, 1, ?i, ?i, ?s, ?i, \'\', 1)', [$adminId, FORK_GROUP_ADMIN, $v->username, $this->c->users->normUsername($v->username), password_hash($v->password, \PASSWORD_DEFAULT), $v->email, $this->c->NormEmail->normalize($v->email), $v->defaultlang, $v->defaultstyle, $now, $now, $ip, $now]);
|
||||
$this->c->DB->exec('INSERT INTO ::categories (id, cat_name, disp_position) VALUES (?i, ?s, ?i)', [$catId, __('Test category'), 1]);
|
||||
$this->c->DB->exec('INSERT INTO ::forums (id, forum_name, forum_desc, num_topics, num_posts, last_post, last_post_id, last_poster, last_poster_id, last_topic, disp_position, cat_id, moderators) VALUES (?i, ?s, ?s, ?i, ?i, ?i, ?i, ?s, ?i, ?s, ?i, ?i, \'\')', [$forumId, __('Test forum'), __('This is just a test forum'), 1, 1, $now, $postId, $v->username, $adminId, __('Test post'), 1, $catId]);
|
||||
$this->c->DB->exec('INSERT INTO ::topics (id, poster, poster_id, subject, posted, first_post_id, last_post, last_post_id, last_poster, last_poster_id, forum_id) VALUES(?i, ?s, ?i, ?s, ?i, ?i, ?i, ?i, ?s, ?i, ?i)', [$topicId, $v->username, $adminId, __('Test post'), $now, $postId, $now, $postId, $v->username, $adminId, $forumId]);
|
||||
$this->c->DB->exec('INSERT INTO ::posts (id, poster, poster_id, poster_ip, message, posted, topic_id) VALUES(?i, ?s, ?i, ?s, ?s, ?i, ?i)', [$postId, $v->username, $adminId, $ip, __('Test message'), $now, $topicId]);
|
||||
$this->c->DB->exec('INSERT INTO ::users (group_id, username, username_normal, password, email, email_normal, language, style, num_posts, last_post, registered, registration_ip, last_visit, signature, num_topics) VALUES (?i, ?s, ?s, ?s, ?s, ?s, ?s, ?s, 1, ?i, ?i, ?s, ?i, \'\', 1)', [FORK_GROUP_ADMIN, $v->username, $this->c->users->normUsername($v->username), password_hash($v->password, \PASSWORD_DEFAULT), $v->email, $this->c->NormEmail->normalize($v->email), $v->defaultlang, $v->defaultstyle, $now, $now, $ip, $now]);
|
||||
|
||||
$adminId = (int) $this->c->DB->lastInsertId();
|
||||
|
||||
$this->c->DB->exec('INSERT INTO ::categories (cat_name, disp_position) VALUES (?s, ?i)', [__('Test category'), 1]);
|
||||
|
||||
$catId = (int) $this->c->DB->lastInsertId();
|
||||
|
||||
$this->c->DB->exec('INSERT INTO ::posts (poster, poster_id, poster_ip, message, posted, topic_id) VALUES(?s, ?i, ?s, ?s, ?i, ?i)', [$v->username, $adminId, $ip, __('Test message'), $now, $topicId]);
|
||||
|
||||
$postId = (int) $this->c->DB->lastInsertId();
|
||||
|
||||
$this->c->DB->exec('INSERT INTO ::forums (forum_name, forum_desc, num_topics, num_posts, last_post, last_post_id, last_poster, last_poster_id, last_topic, disp_position, cat_id, moderators) VALUES (?s, ?s, ?i, ?i, ?i, ?i, ?s, ?i, ?s, ?i, ?i, \'\')', [__('Test forum'), __('This is just a test forum'), 1, 1, $now, $postId, $v->username, $adminId, __('Test post'), 1, $catId]);
|
||||
|
||||
$forumId = (int) $this->c->DB->lastInsertId();
|
||||
|
||||
$this->c->DB->exec('INSERT INTO ::topics (poster, poster_id, subject, posted, first_post_id, last_post, last_post_id, last_poster, last_poster_id, forum_id) VALUES(?s, ?i, ?s, ?i, ?i, ?i, ?i, ?s, ?i, ?i)', [$v->username, $adminId, __('Test post'), $now, $postId, $now, $postId, $v->username, $adminId, $forumId]);
|
||||
|
||||
$topicId = (int) $this->c->DB->lastInsertId();
|
||||
|
||||
$this->c->DB->exec('UPDATE ::posts SET topic_id=?i WHERE id=?i', [$topicId, $postId]);
|
||||
|
||||
$smilies = [
|
||||
':)' => 'smile.png',
|
||||
|
|
|
@ -155,7 +155,8 @@ class Statistics extends Admin
|
|||
$this->dbVersion = $stat['db'];
|
||||
$this->tSize = $stat['size'];
|
||||
$this->tRecords = $stat['records'];
|
||||
unset($stat['db'], $stat['size'], $stat['records']);
|
||||
$this->tTables = $stat['tables'];
|
||||
unset($stat['db'], $stat['size'], $stat['records'], $stat['tables']);
|
||||
$this->tOther = $stat;
|
||||
|
||||
// Check for the existence of various PHP opcode caches/optimizers
|
||||
|
|
|
@ -99,6 +99,9 @@ msgstr "Accelerator:"
|
|||
msgid "Database label"
|
||||
msgstr "Database"
|
||||
|
||||
msgid "Database data tables"
|
||||
msgstr "Tables: %s"
|
||||
|
||||
msgid "Database data rows"
|
||||
msgstr "Rows: %s"
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ msgid "Table prefix error"
|
|||
msgstr "The table prefix '%s' does not match the format."
|
||||
|
||||
msgid "Prefix reserved"
|
||||
msgstr "The table prefix 'sqlite_' is reserved for use by the SQLite engine."
|
||||
msgstr "The 'sqlite_' and 'pg_' prefixes are reserved. Please choose a different prefix."
|
||||
|
||||
msgid "Existing table error"
|
||||
msgstr "A table called '%1$susers' is already present in the database '%2$s'. This could mean that ForkBB is already installed or that another piece of software is installed and is occupying one or more of the table names ForkBB requires. If you want to install multiple copies of ForkBB in the same database, you must choose a different table prefix."
|
||||
|
@ -219,6 +219,15 @@ msgstr "<b>File uploads appear to be disallowed on this server!</b> If you want
|
|||
msgid "Bad database charset"
|
||||
msgstr "The database must be created with the character encoding <b>utf8mb4</b> (compare <b>utf8mb4_unicode_ci</b>)."
|
||||
|
||||
msgid "Bad database encoding"
|
||||
msgstr "The database must be created with the character encoding <b>UTF8</b>."
|
||||
|
||||
msgid "Bad database collate"
|
||||
msgstr "The database must be created with Collation order <b>C</b> (LC_COLLATE)."
|
||||
|
||||
msgid "Bad database ctype"
|
||||
msgstr "The database must be created with Character classification <b>C</b> (LC_CTYPE)."
|
||||
|
||||
msgid "Cookie setup"
|
||||
msgstr "<b>Cookie setup</b>"
|
||||
|
||||
|
|
|
@ -99,6 +99,9 @@ msgstr "Акселератор:"
|
|||
msgid "Database label"
|
||||
msgstr "База данных"
|
||||
|
||||
msgid "Database data tables"
|
||||
msgstr "Таблиц: %s"
|
||||
|
||||
msgid "Database data rows"
|
||||
msgstr "Строк: %s"
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ msgid "Table prefix error"
|
|||
msgstr "Префикс '%s' не соответсвует формату."
|
||||
|
||||
msgid "Prefix reserved"
|
||||
msgstr "Префикс 'sqlite_' зарезрвирован для SQLite."
|
||||
msgstr "Префиксы 'sqlite_' и 'pg_' зарезервированы. Выберите другой префикс."
|
||||
|
||||
msgid "Existing table error"
|
||||
msgstr "Таблица '%1$susers' уже существует в базе '%2$s'. Это может означать, что ForkBB был установлен в данную базу или другой программный пакет занимает таблицы, требуемые для работы ForkBB. Если вы хотите установить несколько копий форума в одну базу, выбирайте разные префиксы для них."
|
||||
|
@ -219,6 +219,15 @@ msgstr "<b>Загрузка файлов, кажется, выключена н
|
|||
msgid "Bad database charset"
|
||||
msgstr "База данных должна быть создана с указанием кодировки символов <b>utf8mb4</b> (сравнение <b>utf8mb4_unicode_ci</b>)."
|
||||
|
||||
msgid "Bad database encoding"
|
||||
msgstr "База данных должна быть создана с указанием кодировки символов <b>UTF8</b>."
|
||||
|
||||
msgid "Bad database collate"
|
||||
msgstr "База данных должна быть создана с порядком сопоставления <b>C</b> (LC_COLLATE)."
|
||||
|
||||
msgid "Bad database ctype"
|
||||
msgstr "База данных должна быть создана с типом символов <b>C</b> (LC_CTYPE)."
|
||||
|
||||
msgid "Cookie setup"
|
||||
msgstr "<b>Настройки cookie</b>"
|
||||
|
||||
|
|
|
@ -24,10 +24,9 @@
|
|||
<dt>{!! __('Database label') !!}</dt>
|
||||
<dd>
|
||||
{{ $p->dbVersion }}
|
||||
@if ($p->tRecords && $p->tSize)
|
||||
<br>{!! __(['Database data tables', $p->tTables]) !!}
|
||||
<br>{!! __(['Database data rows', num($p->tRecords)]) !!}
|
||||
<br>{!! __(['Database data size', size($p->tSize)]) !!}
|
||||
@endif
|
||||
@if ($p->tOther)
|
||||
<br><br>{!! __('Other')!!}
|
||||
@foreach ($p->tOther as $key => $value)
|
||||
|
|
Loading…
Add table
Reference in a new issue