modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2025-04-15 12:12:03 +04:00
parent 2a46c12e08
commit 146461a184
11 changed files with 227 additions and 139 deletions

View File

@ -65,15 +65,29 @@ class CapacitorChannel {
$this->created = false;
$columnDefinitions = cl::withn(static::COLUMN_DEFINITIONS);
$primaryKeys = cl::withn(static::PRIMARY_KEYS);
if ($primaryKeys === null && $columnDefinitions !== null) {
$migration = cl::withn(static::MIGRATION);
if ($columnDefinitions !== null) {
# mettre à jour la liste des clés primaires et des migrations
$index = 0;
foreach ($columnDefinitions as $col => $def) {
if ($col === $index) {
# si définition séquentielle, seules les définitions de clé
# primaires sont supportées
$index++;
if (preg_match('/\bprimary\s+key\s+\((.+)\)/i', $def, $ms)) {
$primaryKeys = preg_split('/\s*,\s*/', trim($ms[1]));
}
} elseif (is_array($def)) {
# tableau: c'est une migration
$def = implode(" ", $def);
if ($def) {
$migration["add_$col"] = "alter table $tableName add column $col $def";
} else {
$migration["drop_$col"] = "alter table $tableName drop column $col";
}
} elseif (is_scalar($def)) {
# chaine: c'est une définition
$def = strval($def);
if (preg_match('/\bprimary\s+key\b/i', $def)) {
$primaryKeys[] = $col;
}
@ -82,7 +96,7 @@ class CapacitorChannel {
}
$this->columnDefinitions = $columnDefinitions;
$this->primaryKeys = $primaryKeys;
$this->migration = cl::withn(static::MIGRATION);
$this->migration = $migration;
}
protected string $name;

View File

@ -47,7 +47,7 @@ abstract class CapacitorStorage {
"modified_" => "serts",
];
protected function ColumnDefinitions(CapacitorChannel $channel): array {
protected function ColumnDefinitions(CapacitorChannel $channel, bool $ignoreMigrations=false): array {
$definitions = [];
if ($channel->getPrimaryKeys() === null) {
$definitions[] = static::PRIMARY_KEY_DEFINITION;
@ -69,9 +69,15 @@ abstract class CapacitorStorage {
if ($col === $index) {
$index++;
$constraints[] = $def;
} else {
} elseif (is_array($def)) {
# éventuellement, ignorer les migrations
$def = implode(" ", $def);
if ($def && !$ignoreMigrations) {
$definitions[$col] = $def;
}
} elseif (is_scalar($def)) {
$definitions[$col] = strval($def);
}
}
return cl::merge($definitions, $constraints);
}
@ -145,7 +151,7 @@ abstract class CapacitorStorage {
return [
"create table if not exists",
"table" => $channel->getTableName(),
"cols" => $this->ColumnDefinitions($channel),
"cols" => $this->ColumnDefinitions($channel, true),
];
}
@ -162,7 +168,37 @@ EOT;
abstract function _getMigration(CapacitorChannel $channel): _migration;
const CHANNELS_TABLE = "_channels";
const CHANNELS_COLS = [
"name" => "varchar primary key",
"table_name" => "varchar",
"class_name" => "varchar",
];
protected function _createChannelsSql(): array {
return [
"create table if not exists",
"table" => static::CHANNELS_TABLE,
"cols" => static::CHANNELS_COLS,
];
}
protected function _addToChannelsSql(CapacitorChannel $channel): array {
return [
"insert",
"into" => static::CHANNELS_TABLE,
"values" => [
"name" => $channel->getName(),
"table_name" => $channel->getTableName(),
"class_name" => get_class($channel),
],
];
}
protected function _afterCreate(CapacitorChannel $channel): void {
$db = $this->db();
$db->exec($this->_createChannelsSql());
$db->exec($this->_addToChannelsSql($channel));
}
protected function _create(CapacitorChannel $channel): void {
@ -191,6 +227,22 @@ EOT;
}
protected function _beforeReset(CapacitorChannel $channel): void {
$db = $this->db;
$name = $channel->getName();
$db->exec([
"delete",
"from" => _migration::MIGRATION_TABLE,
"where" => [
"channel" => $name,
],
]);
$db->exec([
"delete",
"from" => static::CHANNELS_TABLE,
"where" => [
"name" => $name,
],
]);
}
/** supprimer le canal spécifié */

View File

@ -11,9 +11,8 @@ class _create extends _common {
];
static function isa(string $sql): bool {
//return preg_match("/^create(?:\s+table)?\b/i", $sql);
#XXX implémentation minimale
return preg_match("/^create\s+table\b/i", $sql);
return preg_match("/^create(?:\s+table)?\b/i", $sql);
}
static function parse(array $query, ?array &$bindings=null): string {

View File

@ -5,6 +5,14 @@ use nulib\db\IDatabase;
use nulib\php\func;
abstract class _migration {
const MIGRATION_TABLE = "_migration";
const MIGRATION_COLS = [
"channel" => "varchar not null",
"name" => "varchar not null",
"done" => "integer not null default 0",
"primary key (channel, name)",
];
const MIGRATION = null;
function __construct($migrations, string $channel="", ?IDatabase $db=null) {
@ -25,21 +33,40 @@ abstract class _migration {
/** @var callable[]|string[] */
protected $migrations;
abstract function setup(): void;
abstract function beforeMigrate(string $key): bool;
abstract function afterMigrate(string $key): void;
function ensureTable(): void {
$this->db->exec([
"create table if not exists",
"table" => static::MIGRATION_TABLE,
"cols" => static::MIGRATION_COLS,
]);
}
protected function isMigrated(string $name): bool {
return boolval($this->db->get([
"select 1",
"from" => static::MIGRATION_TABLE,
"where" => [
"channel" => $this->channel,
"name" => $name,
"done" => 1,
],
]));
}
abstract protected function setMigrated(string $name, bool $done): void;
function migrate(?IDatabase $db=null): void {
$db = ($this->db ??= $db);
$this->setup();
foreach ($this->migrations as $key => $migration) {
if (!$this->beforeMigrate($key)) continue;
$this->ensureTable();
foreach ($this->migrations as $name => $migration) {
if ($this->isMigrated($name)) continue;
$this->setMigrated($name, false);
if (is_string($migration) || !func::is_callable($migration)) {
$db->exec($migration);
} else {
func::with($migration)->bind($this, true)->invoke([$db, $key]);
func::with($migration)->bind($this, true)->invoke([$db, $name]);
}
$this->afterMigrate($key);
$this->setMigrated($name, true);
}
}
}

View File

@ -1,6 +1,7 @@
<?php
namespace nulib\db\mysql;
use nulib\cl;
use nulib\db\CapacitorChannel;
use nulib\db\CapacitorStorage;
@ -9,26 +10,44 @@ use nulib\db\CapacitorStorage;
*/
class MysqlStorage extends CapacitorStorage {
function __construct($mysql) {
$this->mysql = Mysql::with($mysql);
$this->db = Mysql::with($mysql);
}
protected Mysql $mysql;
protected Mysql $db;
function db(): Mysql {
return $this->mysql;
return $this->db;
}
const PRIMARY_KEY_DEFINITION = [
"id_" => "integer primary key auto_increment",
];
function _getMigration(CapacitorChannel $channel): _mysqlMigration {
return new _mysqlMigration(cl::merge([
$this->_createSql($channel),
], $channel->getMigration()), $channel->getName());
}
function _getCreateSql(CapacitorChannel $channel): string {
$query = new _mysqlQuery($this->_createSql($channel));
return self::format_sql($channel, $query->getSql());
}
const CHANNELS_COLS = [
"name" => "varchar(255) primary key",
"table_name" => "varchar(64)",
"class_name" => "varchar(255)",
];
protected function _addToChannelsSql(CapacitorChannel $channel): array {
return cl::merge(parent::_addToChannelsSql($channel), [
"suffix" => "on duplicate key update name = name",
]);
}
function _exists(CapacitorChannel $channel): bool {
$mysql = $this->mysql;
$mysql = $this->db;
$tableName = $mysql->get([
"select table_name from information_schema.tables",
"where" => [
@ -40,6 +59,6 @@ class MysqlStorage extends CapacitorStorage {
}
function close(): void {
$this->mysql->close();
$this->db->close();
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace nulib\db\mysql;
use nulib\db\_private\_migration;
class _mysqlMigration extends _migration {
const MIGRATION_COLS = [
"channel" => "varchar(64) not null",
"name" => "varchar(64) not null",
"done" => "integer not null default 0",
"primary key (channel, name)",
];
static function with($migration): self {
if ($migration instanceof self) return $migration;
else return new static($migration);
}
protected function setMigrated(string $name, bool $done): void {
$this->db->exec([
"insert",
"into" => static::MIGRATION_TABLE,
"values" => [
"channel" => $this->channel,
"name" => $name,
"done" => $done? 1: 0,
],
"suffix" => "on duplicate key update done = :done",
]);
}
}

View File

@ -4,4 +4,5 @@ namespace nulib\db\mysql;
use nulib\db\pdo\_pdoQuery;
class _mysqlQuery extends _pdoQuery {
const DEBUG_QUERIES = false;
}

View File

@ -1,6 +1,7 @@
<?php
namespace nulib\db\pgsql;
use nulib\cl;
use nulib\db\CapacitorChannel;
use nulib\db\CapacitorStorage;
@ -10,54 +11,33 @@ class PgsqlStorage extends CapacitorStorage {
const SERTS_DEFINITION = "timestamp";
function __construct($pgsql) {
$this->pgsql = Pgsql::with($pgsql);
$this->db = Pgsql::with($pgsql);
}
protected Pgsql $pgsql;
protected Pgsql $db;
function db(): Pgsql {
return $this->pgsql;
return $this->db;
}
const PRIMARY_KEY_DEFINITION = [
"id_" => "serial primary key",
];
function _getMigration(CapacitorChannel $channel): _pgsqlMigration {
return new _pgsqlMigration(cl::merge([
$this->_createSql($channel),
], $channel->getMigration()), $channel->getName());
}
function _getCreateSql(CapacitorChannel $channel): string {
$query = new _pgsqlQuery($this->_createSql($channel));
return self::format_sql($channel, $query->getSql());
}
protected function _afterCreate(CapacitorChannel $channel): void {
$db = $this->pgsql;
$db->exec([
"create table if not exists",
"table" => "_channels",
"cols" => [
"name" => "varchar primary key",
"table_name" => "varchar",
"class" => "varchar",
],
]);
$db->exec([
"insert",
"into" => "_channels",
"values" => [
"name" => $channel->getName(),
"table_name" => $channel->getTableName(),
"class" => get_class($channel),
],
"suffix" => "on conflict (name) do nothing",
]);
}
protected function _beforeReset(CapacitorChannel $channel): void {
$this->pgsql->exec([
"delete",
"from" => "_channels",
"where" => [
"name" => $channel->getName(),
],
protected function _addToChannelsSql(CapacitorChannel $channel): array {
return cl::merge(parent::_addToChannelsSql($channel), [
"suffix" => "on conflict do nothing",
]);
}
@ -69,7 +49,7 @@ class PgsqlStorage extends CapacitorStorage {
} else {
$schemaName = "public";
}
return null !== $this->pgsql->get([
return null !== $this->db->get([
"select tablename from pg_tables",
"where" => [
"schemaname" => $schemaName,
@ -79,6 +59,6 @@ class PgsqlStorage extends CapacitorStorage {
}
function close(): void {
$this->pgsql->close();
$this->db->close();
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace nulib\db\pgsql;
use nulib\db\_private\_migration;
class _pgsqlMigration extends _migration {
static function with($migration): self {
if ($migration instanceof self) return $migration;
else return new static($migration);
}
protected function setMigrated(string $name, bool $done): void {
$this->db->exec([
"insert",
"into" => static::MIGRATION_TABLE,
"values" => [
"channel" => $this->channel,
"name" => $name,
"done" => $done? 1: 0,
],
"suffix" => "on conflict (channel, name) do update set done = :done",
]);
}
}

View File

@ -10,13 +10,13 @@ use nulib\db\CapacitorStorage;
*/
class SqliteStorage extends CapacitorStorage {
function __construct($sqlite) {
$this->sqlite = Sqlite::with($sqlite);
$this->db = Sqlite::with($sqlite);
}
protected Sqlite $sqlite;
protected Sqlite $db;
function db(): Sqlite {
return $this->sqlite;
return $this->db;
}
const PRIMARY_KEY_DEFINITION = [
@ -35,7 +35,7 @@ class SqliteStorage extends CapacitorStorage {
}
function tableExists(string $tableName): bool {
$name = $this->sqlite->get([
$name = $this->db->get([
# depuis la version 3.33.0 le nom officiel de la table est sqlite_schema,
# mais le nom sqlite_master est toujours valable pour le moment
"select name from sqlite_master ",
@ -45,63 +45,38 @@ class SqliteStorage extends CapacitorStorage {
}
function channelExists(string $name): bool {
$name = $this->sqlite->get([
"select name from _channels",
return null !== $this->db->get([
"select name",
"from" => static::CHANNELS_TABLE,
"where" => ["name" => $name],
]);
return $name !== null;
}
protected function _addToChannelsSql(CapacitorChannel $channel): array {
return cl::merge(parent::_createChannelsSql(), [
"suffix" => "on conflict ignore",
]);
}
protected function _afterCreate(CapacitorChannel $channel): void {
$sqlite = $this->sqlite;
if (!$this->tableExists("_channels")) {
$db = $this->db;
if (!$this->tableExists(static::CHANNELS_TABLE)) {
# ne pas créer si la table existe déjà, pour éviter d'avoir besoin d'un
# verrou en écriture
$sqlite->exec([
"create table if not exists _channels",
"cols" => [
"name" => "varchar primary key",
"table_name" => "varchar",
"class" => "varchar",
],
]);
$db->exec($this->_createChannelsSql());
}
if (!$this->channelExists($channel->getName())) {
# ne pas insérer si la ligne existe déjà, pour éviter d'avoir besoin d'un
# verrou en écriture
$sqlite->exec([
"insert into _channels",
"values" => [
"name" => $channel->getName(),
"table_name" => $channel->getTableName(),
"class" => get_class($channel),
],
"suffix" => "on conflict do nothing",
]);
$this->_addToChannelsSql($channel);
}
}
protected function _beforeReset(CapacitorChannel $channel): void {
$sqlite = $this->sqlite;
$sqlite->exec([
"delete from _migration",
"where" => [
"channel" => $channel->getName(),
],
]);
$sqlite->exec([
"delete from _channels",
"where" => [
"name" => $channel->getName(),
],
]);
}
function _exists(CapacitorChannel $channel): bool {
return $this->tableExists($channel->getTableName());
}
function close(): void {
$this->sqlite->close();
$this->db->close();
}
}

View File

@ -9,48 +9,14 @@ class _sqliteMigration extends _migration {
else return new static($migration);
}
function setup(): void {
protected function setMigrated(string $name, bool $done): void {
$this->db->exec([
"create table if not exists _migration",
"cols" => [
"channel" => "varchar not null",
"key" => "varchar not null",
"done" => "integer not null default 0",
],
]);
}
function beforeMigrate(string $key): bool {
$db = $this->db;
$migrated = $db->get([
"select 1 from _migration",
"where" => [
"channel" => $this->channel,
"key" => $key,
"done" => 1,
],
]);
if ($migrated) return false;
$db->exec([
"insert or replace into _migration",
"insert or replace",
"into" => static::MIGRATION_TABLE,
"values" => [
"channel" => $this->channel,
"key" => $key,
"done" => 0,
],
]);
return true;
}
function afterMigrate(string $key): void {
$this->db->exec([
"update _migration",
"values" => [
"done" => 1,
],
"where" => [
"channel" => $this->channel,
"key" => $key,
"name" => $name,
"done" => $done? 1: 0,
],
]);
}