update to 3.2.0, add zm_sqlite() (portable sqlite)

This commit is contained in:
crazywhalecc
2023-06-06 20:23:19 +08:00
committed by Jerry Ma
parent 8866c1de11
commit 731e1d81da
7 changed files with 126 additions and 17 deletions

View File

@@ -55,6 +55,9 @@ const ZM_PLUGIN_TYPE_COMPOSER = 3; // Composer 依赖的插件
const LOAD_MODE_SRC = 0; // 从 src 加载 const LOAD_MODE_SRC = 0; // 从 src 加载
const LOAD_MODE_VENDOR = 1; // 从 vendor 加载 const LOAD_MODE_VENDOR = 1; // 从 vendor 加载
const ZM_DB_POOL = 1; // 数据库连接池
const ZM_DB_PORTABLE = 2; // SQLite 便携数据库
/* 定义工作目录 */ /* 定义工作目录 */
define('WORKING_DIR', getcwd()); define('WORKING_DIR', getcwd());

View File

@@ -25,6 +25,7 @@ use ZM\Plugin\OneBot\BotMap;
use ZM\Plugin\ZMPlugin; use ZM\Plugin\ZMPlugin;
use ZM\Schedule\Timer; use ZM\Schedule\Timer;
use ZM\Store\Database\DBException; use ZM\Store\Database\DBException;
use ZM\Store\Database\DBPool;
use ZM\Store\Database\DBQueryBuilder; use ZM\Store\Database\DBQueryBuilder;
use ZM\Store\Database\DBWrapper; use ZM\Store\Database\DBWrapper;
use ZM\Store\KV\KVInterface; use ZM\Store\KV\KVInterface;
@@ -254,6 +255,32 @@ function sql_builder(string $name = ''): DBQueryBuilder
return (new DBWrapper($name))->createQueryBuilder(); return (new DBWrapper($name))->createQueryBuilder();
} }
/**
* 获取一个便携 SQLite 操作类
*
* @param string $name 使用的 SQLite 连接文件名
* @param bool $create_new 是否在文件不存在时创建新的
* @param bool $keep_alive 是否维持 PDO 对象以便优化性能
* @throws DBException
*/
function zm_sqlite(string $name, bool $create_new = true, bool $keep_alive = true): DBWrapper
{
return DBPool::createPortableSqlite($name, $create_new, $keep_alive);
}
/**
* 获取便携 SQLite 操作类的 SQL 语句构造器
*
* @param string $name 使用的 SQLite 连接文件名
* @param bool $create_new 是否在文件不存在时创建新的
* @param bool $keep_alive 是否维持 PDO 对象以便优化性能
* @throws DBException
*/
function zm_sqlite_builder(string $name, bool $create_new = true, bool $keep_alive = true): DBQueryBuilder
{
return zm_sqlite($name, $create_new, $keep_alive)->createQueryBuilder();
}
/** /**
* 获取 Redis 操作类 * 获取 Redis 操作类
* *

View File

@@ -95,7 +95,7 @@ class WorkerEventListener
}); });
// 注册各种池子 // 注册各种池子
$this->initConnectionPool(); $this->initDBConnections();
// 加载用户代码资源 // 加载用户代码资源
$this->initUserPlugins(); $this->initUserPlugins();
@@ -144,6 +144,7 @@ class WorkerEventListener
if (is_a(config('global.kv.use', \LightCache::class), LightCache::class, true)) { if (is_a(config('global.kv.use', \LightCache::class), LightCache::class, true)) {
LightCache::saveAll(); LightCache::saveAll();
} }
DBPool::resetPortableSQLite();
logger()->debug('{is_task}Worker 进程 #{id} 正在停止', ['is_task' => ProcessStateManager::isTaskWorker() ? 'Task' : '', 'id' => ProcessManager::getProcessId()]); logger()->debug('{is_task}Worker 进程 #{id} 正在停止', ['is_task' => ProcessStateManager::isTaskWorker() ? 'Task' : '', 'id' => ProcessManager::getProcessId()]);
if (Framework::getInstance()->getDriver()->getName() !== 'swoole') { if (Framework::getInstance()->getDriver()->getName() !== 'swoole') {
@@ -260,9 +261,10 @@ class WorkerEventListener
* *
* @throws DBException|RedisException * @throws DBException|RedisException
*/ */
private function initConnectionPool(): void private function initDBConnections(): void
{ {
DBPool::resetPools(); DBPool::resetPools();
DBPool::resetPortableSQLite();
RedisPool::resetPools(); RedisPool::resetPools();
} }
} }

View File

@@ -50,7 +50,7 @@ class Framework
public const VERSION_ID = 720; public const VERSION_ID = 720;
/** @var string 版本名称 */ /** @var string 版本名称 */
public const VERSION = '3.1.14'; public const VERSION = '3.2.0';
/** /**
* @var RuntimePreferences 运行时偏好(环境信息&参数) * @var RuntimePreferences 运行时偏好(环境信息&参数)

View File

@@ -8,25 +8,49 @@ namespace ZM\Store\Database;
use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\ParameterType;
use ZM\Store\FileSystem;
class DBConnection implements Connection class DBConnection implements Connection
{ {
private int $db_type;
/** @var \PDO */ /** @var \PDO */
private object $conn; private object $conn;
private $pool_name; private $pool_name;
public function __construct($params) public function __construct(private array $params)
{ {
logger()->debug('Constructing...'); $this->db_type = $params['dbType'] ?? ZM_DB_POOL;
$this->conn = DBPool::pool($params['dbName'])->get(); if ($params['dbType'] === ZM_DB_POOL) {
$this->pool_name = $params['dbName']; // 默认连接池的形式,
logger()->debug('Constructing...');
$this->conn = DBPool::pool($params['dbName'])->get();
$this->pool_name = $params['dbName'];
} elseif ($params['dbType'] === ZM_DB_PORTABLE) {
$connect_str = 'sqlite:{filename}';
if (FileSystem::isRelativePath($params['filename'])) {
$params['filename'] = zm_dir(config('global.data_dir') . '/db/' . $params['filename']);
FileSystem::createDir(zm_dir(config('global.data_dir') . '/db'));
}
$table = [
'{filename}' => $params['filename'],
];
// 如果文件不存在则创建,但如果设置了 createNew 为 false 则不创建,不存在就直接抛出异常
if (!file_exists($params['filename']) && ($params['createNew'] ?? true) === false) {
throw new DBException("Database file {$params['filename']} not found!");
}
$connect_str = str_replace(array_keys($table), array_values($table), $connect_str);
$this->conn = new \PDO($connect_str);
}
} }
public function __destruct() public function __destruct()
{ {
logger()->debug('Destructing'); if ($this->db_type === ZM_DB_POOL) {
DBPool::pool($this->pool_name)->put($this->conn); logger()->debug('Destructing');
DBPool::pool($this->pool_name)->put($this->conn);
}
} }
/** /**
@@ -126,4 +150,14 @@ class DBConnection implements Connection
{ {
return $this->pool_name; return $this->pool_name;
} }
public function getDbType(): int
{
return $this->db_type;
}
public function getParams(): array
{
return $this->params;
}
} }

View File

@@ -21,6 +21,11 @@ class DBPool
*/ */
private static array $pools = []; private static array $pools = [];
/**
* @var array<string, DBWrapper> 持久化的便携 SQLite 连接对象缓存
*/
private static array $portable_cache = [];
/** /**
* 重新初始化连接池,有时候连不上某个对象时候可以使用,也可以定期调用释放链接 * 重新初始化连接池,有时候连不上某个对象时候可以使用,也可以定期调用释放链接
* *
@@ -43,6 +48,16 @@ class DBPool
} }
} }
/**
* 重新初始化所有的便携 SQLite 连接(其实就是断开)
*/
public static function resetPortableSQLite(): void
{
foreach (self::$portable_cache as $name => $wrapper) {
unset(self::$portable_cache[$name]);
}
}
/** /**
* 通过配置文件创建一个 MySQL 连接池 * 通过配置文件创建一个 MySQL 连接池
* *
@@ -180,4 +195,23 @@ class DBPool
} }
} }
} }
/**
* 创建一个便携的 SQLite 处理类
*
* @param string $name SQLite 文件名
* @param bool $create_new 如果数据库不存在,是否创建新的库
* @throws DBException
*/
public static function createPortableSqlite(string $name, bool $create_new = true, bool $keep_alive = true): DBWrapper
{
if ($keep_alive && isset(self::$portable_cache[$name])) {
return self::$portable_cache[$name];
}
$db = new DBWrapper($name, ['dbType' => ZM_DB_PORTABLE, 'createNew' => $create_new]);
if ($keep_alive) {
self::$portable_cache[$name] = $db;
}
return $db;
}
} }

View File

@@ -18,17 +18,25 @@ class DBWrapper
* DBWrapper constructor. * DBWrapper constructor.
* @throws DBException * @throws DBException
*/ */
public function __construct(string $name) public function __construct(string $name, array $options = [])
{ {
// 初始化配置
$db_type = $options['dbType'] ?? ZM_DB_POOL;
try { try {
$db_list = config()->get('global.database'); if ($db_type === ZM_DB_POOL) {
if (isset($db_list[$name]) || (is_countable($db_list) ? count($db_list) : 0) === 1) { // pool 为连接池格式
if ($name === '') { $db_list = config()->get('global.database');
$name = array_key_first($db_list); if (isset($db_list[$name]) || (is_countable($db_list) ? count($db_list) : 0) === 1) {
if ($name === '') {
$name = array_key_first($db_list);
}
$this->connection = DriverManager::getConnection(['driverClass' => $this->getConnectionClass($db_list[$name]['type']), ...$options]);
} else {
throw new DBException('Cannot find database config named "' . $name . '" !');
} }
$this->connection = DriverManager::getConnection(['driverClass' => $this->getConnectionClass($db_list[$name]['type']), 'dbName' => $name]); } elseif ($db_type === ZM_DB_PORTABLE) {
} else { // portable 为sqlite单文件模式
throw new DBException('Cannot find database config named "' . $name . '" !'); $this->connection = DriverManager::getConnection(['driverClass' => SQLiteDriver::class, 'filename' => $name, ...$options]);
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
throw new DBException($e->getMessage(), $e->getCode(), $e); throw new DBException($e->getMessage(), $e->getCode(), $e);
@@ -38,6 +46,7 @@ class DBWrapper
public function __destruct() public function __destruct()
{ {
$this->connection->close(); $this->connection->close();
$this->connection->close();
} }
/** /**