From d241ce656113d43e3257b3da69c468f1ed3c3d32 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Mon, 14 Apr 2025 10:42:30 +0400 Subject: [PATCH] ajout PgsqlStorage --- pgsql.php | 81 ++++++++++++++++++++++ php/src/db/CapacitorChannel.php | 3 - php/src/db/CapacitorStorage.php | 21 ++++-- php/src/db/mysql/MysqlStorage.php | 15 ++-- php/src/db/pdo/Pdo.php | 6 +- php/src/db/pdo/query.php | 9 +-- php/src/db/pgsql/PgsqlStorage.php | 84 +++++++++++++++++++++++ php/src/db/pgsql/query.php | 7 +- php/src/db/sqlite/Sqlite.php | 6 +- php/src/db/sqlite/SqliteStorage.php | 21 +++--- php/src/db/sqlite/query.php | 7 +- php/src/php/func.php | 46 +++++-------- php/tests/db/sqlite/SqliteStorageTest.php | 2 +- 13 files changed, 235 insertions(+), 73 deletions(-) create mode 100644 pgsql.php create mode 100644 php/src/db/pgsql/PgsqlStorage.php diff --git a/pgsql.php b/pgsql.php new file mode 100644 index 0000000..918568f --- /dev/null +++ b/pgsql.php @@ -0,0 +1,81 @@ + "pegase-dre.self", + "dbname" => "dre", + "user" => "root", + "password" => "admin", + #"user" => "reader", + #"password" => "reader", +]); + +function e($sql) { + global $pgsql; + $v = $pgsql->exec($sql); + $v = var_export($v, true); + echo "'$sql' --> $v\n"; +} +function g($sql) { + global $pgsql; + $v = $pgsql->get($sql); + $v = var_export($v, true); + echo "'$sql' --> $v\n"; +} +function o($sql) { + global $pgsql; + $r = $pgsql->one($sql); + $r = var_export($r, true); + echo "'$sql' --> $r\n"; +} +function a($sql) { + global $pgsql; + $rs = $pgsql->all($sql); + echo "'$sql'\n"; + foreach ($rs as $r) { + $r = var_export($r, true); + echo " --> $r\n"; + } +} + +g("select age from personnes where id=1"); +o("select name, age from personnes where id=2"); +a("select id, name, age from personnes"); + +$n = rand(); +$pgsql->exec([ + "insert", + "into" => "personnes", + "values" => [ + "name" => "prout$n", + "age" => $n, + ], +]); + +class MyChannel extends CapacitorChannel { + const COLUMN_DEFINITIONS = [ + "name" => "varchar", + "value" => "int", + ]; + + function getItemValues($item): ?array { + return $item; + } +} +$storage = new PgsqlStorage($pgsql); +$channel = new MyChannel(); +new Capacitor($storage, $channel); + +$channel->charge([ + "name" => "one", + "value" => rand(), +]); +foreach ($channel->all(null) as $row) { + var_export($row); +} \ No newline at end of file diff --git a/php/src/db/CapacitorChannel.php b/php/src/db/CapacitorChannel.php index 4495074..97819d4 100644 --- a/php/src/db/CapacitorChannel.php +++ b/php/src/db/CapacitorChannel.php @@ -245,9 +245,6 @@ class CapacitorChannel { return $serial !== null? unserialize($serial): null; } - const SERIAL_DEFINITION = "mediumtext"; - const SUM_DEFINITION = "varchar(40)"; - final function sum(?string $serial, $value=null): ?string { if ($serial === null) $serial = $this->serialize($value); return $serial !== null? sha1($serial): null; diff --git a/php/src/db/CapacitorStorage.php b/php/src/db/CapacitorStorage.php index 1f6c759..830e032 100644 --- a/php/src/db/CapacitorStorage.php +++ b/php/src/db/CapacitorStorage.php @@ -35,11 +35,15 @@ abstract class CapacitorStorage { /** DOIT être défini dans les classes dérivées */ const PRIMARY_KEY_DEFINITION = null; + const SERDATA_DEFINITION = "mediumtext"; + const SERSUM_DEFINITION = "varchar(40)"; + const SERTS_DEFINITION = "datetime"; + const COLUMN_DEFINITIONS = [ - "item__" => CapacitorChannel::SERIAL_DEFINITION, - "item__sum_" => CapacitorChannel::SUM_DEFINITION, - "created_" => "datetime", - "modified_" => "datetime", + "item__" => "serdata", + "item__sum_" => "sersum", + "created_" => "serts", + "modified_" => "serts", ]; protected function ColumnDefinitions(CapacitorChannel $channel): array { @@ -56,6 +60,11 @@ abstract class CapacitorStorage { $constraints = []; $index = 0; foreach ($tmp as $col => $def) { + switch ($def) { + case "serdata": $def = static::SERDATA_DEFINITION; break; + case "sersum": $def = static::SERSUM_DEFINITION; break; + case "serts": $def = static::SERTS_DEFINITION; break; + } if ($col === $index) { $index++; $constraints[] = $def; @@ -160,7 +169,7 @@ EOT; protected function _create(CapacitorChannel $channel): void { $channel->ensureSetup(); if (!$channel->isCreated()) { - $this->db->exec($this->_createSql($channel)); + $this->db()->exec($this->_createSql($channel)); $this->_afterCreate($channel); $channel->setCreated(); } @@ -188,7 +197,7 @@ EOT; /** supprimer le canal spécifié */ function _reset(CapacitorChannel $channel, bool $recreate=false): void { $this->_beforeReset($channel); - $this->db->exec([ + $this->db()->exec([ "drop table if exists", $channel->getTableName(), ]); diff --git a/php/src/db/mysql/MysqlStorage.php b/php/src/db/mysql/MysqlStorage.php index 4d8cab5..0c5e0f6 100644 --- a/php/src/db/mysql/MysqlStorage.php +++ b/php/src/db/mysql/MysqlStorage.php @@ -9,14 +9,13 @@ use nulib\db\CapacitorStorage; */ class MysqlStorage extends CapacitorStorage { function __construct($mysql) { - $this->db = Mysql::with($mysql); + $this->mysql = Mysql::with($mysql); } - /** @var Mysql */ - protected $db; + protected Mysql $mysql; function db(): Mysql { - return $this->db; + return $this->mysql; } const PRIMARY_KEY_DEFINITION = [ @@ -29,11 +28,11 @@ class MysqlStorage extends CapacitorStorage { } function _exists(CapacitorChannel $channel): bool { - $db = $this->db; - $tableName = $db->get([ + $mysql = $this->mysql; + $tableName = $mysql->get([ "select table_name from information_schema.tables", "where" => [ - "table_schema" => $db->getDbname(), + "table_schema" => $mysql->getDbname(), "table_name" => $channel->getTableName(), ], ]); @@ -41,6 +40,6 @@ class MysqlStorage extends CapacitorStorage { } function close(): void { - $this->db->close(); + $this->mysql->close(); } } diff --git a/php/src/db/pdo/Pdo.php b/php/src/db/pdo/Pdo.php index 286dbeb..091050a 100644 --- a/php/src/db/pdo/Pdo.php +++ b/php/src/db/pdo/Pdo.php @@ -146,7 +146,7 @@ class Pdo implements IDatabase { function exec($query, ?array $params=null) { $db = $this->db(); $query = new query($query, $params); - if ($query->useStmt($db, $stmt, $sql)) { + if ($query->_use_stmt($db, $stmt, $sql)) { if ($stmt->execute() === false) return false; if ($query->isInsert()) return $db->lastInsertId(); else return $stmt->rowCount(); @@ -221,7 +221,7 @@ class Pdo implements IDatabase { $stmt = null; try { /** @var \PDOStatement $stmt */ - if ($query->useStmt($db, $stmt, $sql)) { + if ($query->_use_stmt($db, $stmt, $sql)) { if ($stmt->execute() === false) return null; } else { $stmt = $db->query($sql); @@ -246,7 +246,7 @@ class Pdo implements IDatabase { $stmt = null; try { /** @var \PDOStatement $stmt */ - if ($query->useStmt($db, $stmt, $sql)) { + if ($query->_use_stmt($db, $stmt, $sql)) { if ($stmt->execute() === false) return; } else { $stmt = $db->query($sql); diff --git a/php/src/db/pdo/query.php b/php/src/db/pdo/query.php index 5d6962f..6fb05c9 100644 --- a/php/src/db/pdo/query.php +++ b/php/src/db/pdo/query.php @@ -3,16 +3,17 @@ namespace nulib\db\pdo; use nulib\db\_private\_base; use nulib\db\_private\Tbindings; +use nulib\output\msg; class query extends _base { use Tbindings; const DEBUG_QUERIES = false; - function useStmt(\PDO $db, ?\PDOStatement &$stmt=null, ?string &$sql=null): bool { - if (static::DEBUG_QUERIES) { #XXX - error_log($this->sql); - //error_log(var_export($this->bindings, true)); + function _use_stmt(\PDO $db, ?\PDOStatement &$stmt=null, ?string &$sql=null): bool { + if (static::DEBUG_QUERIES) {#XXX + msg::info($this->sql); + //msg::info(var_export($this->bindings, true)); } if ($this->bindings !== null) { $stmt = $db->prepare($this->sql); diff --git a/php/src/db/pgsql/PgsqlStorage.php b/php/src/db/pgsql/PgsqlStorage.php new file mode 100644 index 0000000..f47078a --- /dev/null +++ b/php/src/db/pgsql/PgsqlStorage.php @@ -0,0 +1,84 @@ +pgsql = Pgsql::with($pgsql); + } + + protected Pgsql $pgsql; + + function db(): Pgsql { + return $this->pgsql; + } + + const PRIMARY_KEY_DEFINITION = [ + "id_" => "serial primary key", + ]; + + function _getCreateSql(CapacitorChannel $channel): string { + $query = new query($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(), + ], + ]); + } + + function _exists(CapacitorChannel $channel): bool { + $tableName = $channel->getTableName(); + if (($index = strpos($tableName, ".")) !== false) { + $schemaName = substr($tableName, 0, $index); + $tableName = substr($tableName, $index + 1); + } else { + $schemaName = "public"; + } + return null !== $this->pgsql->get([ + "select tablename from pg_tables", + "where" => [ + "schemaname" => $schemaName, + "tablename" => $tableName, + ], + ]); + } + + function close(): void { + $this->pgsql->close(); + } +} diff --git a/php/src/db/pgsql/query.php b/php/src/db/pgsql/query.php index c1c11ba..3ab5b6d 100644 --- a/php/src/db/pgsql/query.php +++ b/php/src/db/pgsql/query.php @@ -4,6 +4,7 @@ namespace nulib\db\pgsql; use nulib\cv; use nulib\db\_private\_base; use nulib\db\_private\Tbindings; +use nulib\output\msg; class query extends _base { use Tbindings; @@ -16,9 +17,9 @@ class query extends _base { function _exec($db) { $sql = $this->sql; $bindings = $this->bindings; - if (static::DEBUG_QUERIES) { #XXX - error_log($sql); - //error_log(var_export($this->bindings, true)); + if (static::DEBUG_QUERIES) {#XXX + msg::info($sql); + //msg::info(var_export($bindings, true)); } if ($bindings !== null) { # trier d'abord les champ par ordre de longueur, pour éviter les overlaps diff --git a/php/src/db/sqlite/Sqlite.php b/php/src/db/sqlite/Sqlite.php index 47ed23a..4e22e11 100644 --- a/php/src/db/sqlite/Sqlite.php +++ b/php/src/db/sqlite/Sqlite.php @@ -184,7 +184,7 @@ class Sqlite implements IDatabase { function exec($query, ?array $params=null) { $db = $this->db(); $query = new query($query, $params); - if ($query->useStmt($db, $stmt, $sql)) { + if ($query->_use_stmt($db, $stmt, $sql)) { try { $result = $stmt->execute(); if ($result === false) return false; @@ -271,7 +271,7 @@ class Sqlite implements IDatabase { function get($query, ?array $params=null, bool $entireRow=false) { $db = $this->db(); $query = new query($query, $params); - if ($query->useStmt($db, $stmt, $sql)) { + if ($query->_use_stmt($db, $stmt, $sql)) { try { $result = $this->checkResult($stmt->execute()); try { @@ -316,7 +316,7 @@ class Sqlite implements IDatabase { function all($query, ?array $params=null, $primaryKeys=null): iterable { $db = $this->db(); $query = new query($query, $params); - if ($query->useStmt($db, $stmt, $sql)) { + if ($query->_use_stmt($db, $stmt, $sql)) { $result = $this->checkResult($stmt->execute()); return $this->_fetchResult($result, $stmt, $primaryKeys); } else { diff --git a/php/src/db/sqlite/SqliteStorage.php b/php/src/db/sqlite/SqliteStorage.php index b29639b..d304d93 100644 --- a/php/src/db/sqlite/SqliteStorage.php +++ b/php/src/db/sqlite/SqliteStorage.php @@ -9,14 +9,13 @@ use nulib\db\CapacitorStorage; */ class SqliteStorage extends CapacitorStorage { function __construct($sqlite) { - $this->db = Sqlite::with($sqlite); + $this->sqlite = Sqlite::with($sqlite); } - /** @var Sqlite */ - protected $db; + protected Sqlite $sqlite; function db(): Sqlite { - return $this->db; + return $this->sqlite; } const PRIMARY_KEY_DEFINITION = [ @@ -29,7 +28,7 @@ class SqliteStorage extends CapacitorStorage { } function tableExists(string $tableName): bool { - $name = $this->db->get([ + $name = $this->sqlite->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 ", @@ -39,7 +38,7 @@ class SqliteStorage extends CapacitorStorage { } function channelExists(string $name): bool { - $name = $this->db->get([ + $name = $this->sqlite->get([ "select name from _channels", "where" => ["name" => $name], ]); @@ -47,11 +46,11 @@ class SqliteStorage extends CapacitorStorage { } protected function _afterCreate(CapacitorChannel $channel): void { - $db = $this->db; + $sqlite = $this->sqlite; if (!$this->tableExists("_channels")) { # ne pas créer si la table existe déjà, pour éviter d'avoir besoin d'un # verrou en écriture - $db->exec([ + $sqlite->exec([ "create table if not exists", "table" => "_channels", "cols" => [ @@ -64,7 +63,7 @@ class SqliteStorage extends CapacitorStorage { 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 - $db->exec([ + $sqlite->exec([ "insert", "into" => "_channels", "values" => [ @@ -78,7 +77,7 @@ class SqliteStorage extends CapacitorStorage { } protected function _beforeReset(CapacitorChannel $channel): void { - $this->db->exec([ + $this->sqlite->exec([ "delete", "from" => "_channels", "where" => [ @@ -92,6 +91,6 @@ class SqliteStorage extends CapacitorStorage { } function close(): void { - $this->db->close(); + $this->sqlite->close(); } } diff --git a/php/src/db/sqlite/query.php b/php/src/db/sqlite/query.php index 294d583..e8df936 100644 --- a/php/src/db/sqlite/query.php +++ b/php/src/db/sqlite/query.php @@ -19,8 +19,11 @@ class query extends _base { const DEBUG_QUERIES = false; - function useStmt(SQLite3 $db, ?SQLite3Stmt &$stmt=null, ?string &$sql=null): bool { - if (static::DEBUG_QUERIES) msg::info($this->sql); #XXX + function _use_stmt(SQLite3 $db, ?SQLite3Stmt &$stmt=null, ?string &$sql=null): bool { + if (static::DEBUG_QUERIES) {#XXX + msg::info($this->sql); + //msg::info(var_export($this->bindings, true)); + } if ($this->bindings !== null) { /** @var SQLite3Stmt $stmt */ $stmt = SqliteException::check($db, $db->prepare($this->sql)); diff --git a/php/src/php/func.php b/php/src/php/func.php index 79e9c78..1309ec8 100644 --- a/php/src/php/func.php +++ b/php/src/php/func.php @@ -61,10 +61,7 @@ class func { * la fonction (ne pas uniquement faire une vérification syntaxique) */ static function verifix_function(&$func, bool $strict=true, ?string &$reason=null): bool { - if ($strict) { - $msg = var_export($func, true); - $reason = null; - } + if ($strict) $reason = null; if ($func instanceof ReflectionFunction) return true; if (is_string($func)) { $c = false; @@ -85,11 +82,11 @@ class func { if ($strict) { $reason = null; if (class_exists($f)) { - $reason = "$msg: is a class"; + $reason = "$f: is a class"; return false; } if (!function_exists($f)) { - $reason = "$msg: function not found"; + $reason = "$f: function not found"; return false; } } @@ -120,10 +117,7 @@ class func { * faire une vérification syntaxique) */ static function verifix_class(&$func, bool $strict=true, ?string &$reason=null): bool { - if ($strict) { - $msg = var_export($func, true); - $reason = null; - } + if ($strict) $reason = null; if ($func instanceof ReflectionClass) return true; if (is_string($func)) { $c = $func; @@ -141,11 +135,9 @@ class func { if (self::_parse_static($c)) return false; if (self::_parse_method($c)) return false; if ($f !== false) return false; - if ($strict) { - if (!class_exists($c)) { - $reason = "$msg: class not found"; - return false; - } + if ($strict && !class_exists($c)) { + $reason = "$c: class not found"; + return false; } $func = [$c, false]; return true; @@ -210,10 +202,7 @@ class func { * la méthode est liée (ne pas uniquement faire une vérification syntaxique) */ static function verifix_static(&$func, bool $strict=true, ?bool &$bound=null, ?string &$reason=null): bool { - if ($strict) { - $msg = var_export($func, true); - $reason = null; - } + if ($strict) $reason = null; if ($func instanceof ReflectionMethod) { $bound = false; return true; @@ -268,18 +257,19 @@ class func { return false; } if ($strict) { + [$c, $f] = $cf; $reason = null; if ($bound) { if (!class_exists($c)) { - $reason = "$msg: class not found"; + $reason = "$c: class not found"; return false; } if (!method_exists($c, $f)) { - $reason = "$msg: method not found"; + $reason = "$c::$f: method not found"; return false; } } else { - $reason = "$msg: not bound"; + $reason = "$c::$f: not bound"; } } $func = $cf; @@ -345,10 +335,7 @@ class func { * la méthode est liée (ne pas uniquement faire une vérification syntaxique) */ static function verifix_method(&$func, bool $strict=true, ?bool &$bound=null, ?string &$reason=null): bool { - if ($strict) { - $msg = var_export($func, true); - $reason = null; - } + if ($strict) $reason = null; if ($func instanceof ReflectionMethod) { $bound = false; return true; @@ -404,18 +391,19 @@ class func { return false; } if ($strict) { + [$c, $f] = $cf; $reason = null; if ($bound) { if (!is_object($c) && !class_exists($c)) { - $reason = "$msg: class not found"; + $reason = "$c: class not found"; return false; } if (!method_exists($c, $f)) { - $reason = "$msg: method not found"; + $reason = "$c::$f: method not found"; return false; } } else { - $reason = "$msg: not bound"; + $reason = "$c::$f: not bound"; } } $func = $cf; diff --git a/php/tests/db/sqlite/SqliteStorageTest.php b/php/tests/db/sqlite/SqliteStorageTest.php index e40ccdf..e7197a3 100644 --- a/php/tests/db/sqlite/SqliteStorageTest.php +++ b/php/tests/db/sqlite/SqliteStorageTest.php @@ -118,7 +118,7 @@ class SqliteStorageTest extends TestCase { const COLUMN_DEFINITIONS = [ "a__" => "varchar", "b__" => "varchar", - "b__sum_" => self::SUM_DEFINITION, + "b__sum_" => "sersum", ]; function getItemValues($item): ?array {