ajout PgsqlStorage

This commit is contained in:
Jephté Clain 2025-04-14 10:42:30 +04:00
parent 86136e75a5
commit d241ce6561
13 changed files with 235 additions and 73 deletions

81
pgsql.php Normal file
View File

@ -0,0 +1,81 @@
<?php
require __DIR__.'/vendor/autoload.php';
use nulib\cl;
use nulib\db\Capacitor;
use nulib\db\CapacitorChannel;
use nulib\db\pgsql\Pgsql;
use nulib\db\pgsql\PgsqlStorage;
$pgsql = new Pgsql([
"host" => "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);
}

View File

@ -245,9 +245,6 @@ class CapacitorChannel {
return $serial !== null? unserialize($serial): null; return $serial !== null? unserialize($serial): null;
} }
const SERIAL_DEFINITION = "mediumtext";
const SUM_DEFINITION = "varchar(40)";
final function sum(?string $serial, $value=null): ?string { final function sum(?string $serial, $value=null): ?string {
if ($serial === null) $serial = $this->serialize($value); if ($serial === null) $serial = $this->serialize($value);
return $serial !== null? sha1($serial): null; return $serial !== null? sha1($serial): null;

View File

@ -35,11 +35,15 @@ abstract class CapacitorStorage {
/** DOIT être défini dans les classes dérivées */ /** DOIT être défini dans les classes dérivées */
const PRIMARY_KEY_DEFINITION = null; const PRIMARY_KEY_DEFINITION = null;
const SERDATA_DEFINITION = "mediumtext";
const SERSUM_DEFINITION = "varchar(40)";
const SERTS_DEFINITION = "datetime";
const COLUMN_DEFINITIONS = [ const COLUMN_DEFINITIONS = [
"item__" => CapacitorChannel::SERIAL_DEFINITION, "item__" => "serdata",
"item__sum_" => CapacitorChannel::SUM_DEFINITION, "item__sum_" => "sersum",
"created_" => "datetime", "created_" => "serts",
"modified_" => "datetime", "modified_" => "serts",
]; ];
protected function ColumnDefinitions(CapacitorChannel $channel): array { protected function ColumnDefinitions(CapacitorChannel $channel): array {
@ -56,6 +60,11 @@ abstract class CapacitorStorage {
$constraints = []; $constraints = [];
$index = 0; $index = 0;
foreach ($tmp as $col => $def) { 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) { if ($col === $index) {
$index++; $index++;
$constraints[] = $def; $constraints[] = $def;
@ -160,7 +169,7 @@ EOT;
protected function _create(CapacitorChannel $channel): void { protected function _create(CapacitorChannel $channel): void {
$channel->ensureSetup(); $channel->ensureSetup();
if (!$channel->isCreated()) { if (!$channel->isCreated()) {
$this->db->exec($this->_createSql($channel)); $this->db()->exec($this->_createSql($channel));
$this->_afterCreate($channel); $this->_afterCreate($channel);
$channel->setCreated(); $channel->setCreated();
} }
@ -188,7 +197,7 @@ EOT;
/** supprimer le canal spécifié */ /** supprimer le canal spécifié */
function _reset(CapacitorChannel $channel, bool $recreate=false): void { function _reset(CapacitorChannel $channel, bool $recreate=false): void {
$this->_beforeReset($channel); $this->_beforeReset($channel);
$this->db->exec([ $this->db()->exec([
"drop table if exists", "drop table if exists",
$channel->getTableName(), $channel->getTableName(),
]); ]);

View File

@ -9,14 +9,13 @@ use nulib\db\CapacitorStorage;
*/ */
class MysqlStorage extends CapacitorStorage { class MysqlStorage extends CapacitorStorage {
function __construct($mysql) { function __construct($mysql) {
$this->db = Mysql::with($mysql); $this->mysql = Mysql::with($mysql);
} }
/** @var Mysql */ protected Mysql $mysql;
protected $db;
function db(): Mysql { function db(): Mysql {
return $this->db; return $this->mysql;
} }
const PRIMARY_KEY_DEFINITION = [ const PRIMARY_KEY_DEFINITION = [
@ -29,11 +28,11 @@ class MysqlStorage extends CapacitorStorage {
} }
function _exists(CapacitorChannel $channel): bool { function _exists(CapacitorChannel $channel): bool {
$db = $this->db; $mysql = $this->mysql;
$tableName = $db->get([ $tableName = $mysql->get([
"select table_name from information_schema.tables", "select table_name from information_schema.tables",
"where" => [ "where" => [
"table_schema" => $db->getDbname(), "table_schema" => $mysql->getDbname(),
"table_name" => $channel->getTableName(), "table_name" => $channel->getTableName(),
], ],
]); ]);
@ -41,6 +40,6 @@ class MysqlStorage extends CapacitorStorage {
} }
function close(): void { function close(): void {
$this->db->close(); $this->mysql->close();
} }
} }

View File

@ -146,7 +146,7 @@ class Pdo implements IDatabase {
function exec($query, ?array $params=null) { function exec($query, ?array $params=null) {
$db = $this->db(); $db = $this->db();
$query = new query($query, $params); $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 ($stmt->execute() === false) return false;
if ($query->isInsert()) return $db->lastInsertId(); if ($query->isInsert()) return $db->lastInsertId();
else return $stmt->rowCount(); else return $stmt->rowCount();
@ -221,7 +221,7 @@ class Pdo implements IDatabase {
$stmt = null; $stmt = null;
try { try {
/** @var \PDOStatement $stmt */ /** @var \PDOStatement $stmt */
if ($query->useStmt($db, $stmt, $sql)) { if ($query->_use_stmt($db, $stmt, $sql)) {
if ($stmt->execute() === false) return null; if ($stmt->execute() === false) return null;
} else { } else {
$stmt = $db->query($sql); $stmt = $db->query($sql);
@ -246,7 +246,7 @@ class Pdo implements IDatabase {
$stmt = null; $stmt = null;
try { try {
/** @var \PDOStatement $stmt */ /** @var \PDOStatement $stmt */
if ($query->useStmt($db, $stmt, $sql)) { if ($query->_use_stmt($db, $stmt, $sql)) {
if ($stmt->execute() === false) return; if ($stmt->execute() === false) return;
} else { } else {
$stmt = $db->query($sql); $stmt = $db->query($sql);

View File

@ -3,16 +3,17 @@ namespace nulib\db\pdo;
use nulib\db\_private\_base; use nulib\db\_private\_base;
use nulib\db\_private\Tbindings; use nulib\db\_private\Tbindings;
use nulib\output\msg;
class query extends _base { class query extends _base {
use Tbindings; use Tbindings;
const DEBUG_QUERIES = false; const DEBUG_QUERIES = false;
function useStmt(\PDO $db, ?\PDOStatement &$stmt=null, ?string &$sql=null): bool { function _use_stmt(\PDO $db, ?\PDOStatement &$stmt=null, ?string &$sql=null): bool {
if (static::DEBUG_QUERIES) {#XXX if (static::DEBUG_QUERIES) {#XXX
error_log($this->sql); msg::info($this->sql);
//error_log(var_export($this->bindings, true)); //msg::info(var_export($this->bindings, true));
} }
if ($this->bindings !== null) { if ($this->bindings !== null) {
$stmt = $db->prepare($this->sql); $stmt = $db->prepare($this->sql);

View File

@ -0,0 +1,84 @@
<?php
namespace nulib\db\pgsql;
use nulib\db\CapacitorChannel;
use nulib\db\CapacitorStorage;
class PgsqlStorage extends CapacitorStorage {
const SERDATA_DEFINITION = "text";
const SERSUM_DEFINITION = "varchar(40)";
const SERTS_DEFINITION = "timestamp";
function __construct($pgsql) {
$this->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();
}
}

View File

@ -4,6 +4,7 @@ namespace nulib\db\pgsql;
use nulib\cv; use nulib\cv;
use nulib\db\_private\_base; use nulib\db\_private\_base;
use nulib\db\_private\Tbindings; use nulib\db\_private\Tbindings;
use nulib\output\msg;
class query extends _base { class query extends _base {
use Tbindings; use Tbindings;
@ -17,8 +18,8 @@ class query extends _base {
$sql = $this->sql; $sql = $this->sql;
$bindings = $this->bindings; $bindings = $this->bindings;
if (static::DEBUG_QUERIES) {#XXX if (static::DEBUG_QUERIES) {#XXX
error_log($sql); msg::info($sql);
//error_log(var_export($this->bindings, true)); //msg::info(var_export($bindings, true));
} }
if ($bindings !== null) { if ($bindings !== null) {
# trier d'abord les champ par ordre de longueur, pour éviter les overlaps # trier d'abord les champ par ordre de longueur, pour éviter les overlaps

View File

@ -184,7 +184,7 @@ class Sqlite implements IDatabase {
function exec($query, ?array $params=null) { function exec($query, ?array $params=null) {
$db = $this->db(); $db = $this->db();
$query = new query($query, $params); $query = new query($query, $params);
if ($query->useStmt($db, $stmt, $sql)) { if ($query->_use_stmt($db, $stmt, $sql)) {
try { try {
$result = $stmt->execute(); $result = $stmt->execute();
if ($result === false) return false; if ($result === false) return false;
@ -271,7 +271,7 @@ class Sqlite implements IDatabase {
function get($query, ?array $params=null, bool $entireRow=false) { function get($query, ?array $params=null, bool $entireRow=false) {
$db = $this->db(); $db = $this->db();
$query = new query($query, $params); $query = new query($query, $params);
if ($query->useStmt($db, $stmt, $sql)) { if ($query->_use_stmt($db, $stmt, $sql)) {
try { try {
$result = $this->checkResult($stmt->execute()); $result = $this->checkResult($stmt->execute());
try { try {
@ -316,7 +316,7 @@ class Sqlite implements IDatabase {
function all($query, ?array $params=null, $primaryKeys=null): iterable { function all($query, ?array $params=null, $primaryKeys=null): iterable {
$db = $this->db(); $db = $this->db();
$query = new query($query, $params); $query = new query($query, $params);
if ($query->useStmt($db, $stmt, $sql)) { if ($query->_use_stmt($db, $stmt, $sql)) {
$result = $this->checkResult($stmt->execute()); $result = $this->checkResult($stmt->execute());
return $this->_fetchResult($result, $stmt, $primaryKeys); return $this->_fetchResult($result, $stmt, $primaryKeys);
} else { } else {

View File

@ -9,14 +9,13 @@ use nulib\db\CapacitorStorage;
*/ */
class SqliteStorage extends CapacitorStorage { class SqliteStorage extends CapacitorStorage {
function __construct($sqlite) { function __construct($sqlite) {
$this->db = Sqlite::with($sqlite); $this->sqlite = Sqlite::with($sqlite);
} }
/** @var Sqlite */ protected Sqlite $sqlite;
protected $db;
function db(): Sqlite { function db(): Sqlite {
return $this->db; return $this->sqlite;
} }
const PRIMARY_KEY_DEFINITION = [ const PRIMARY_KEY_DEFINITION = [
@ -29,7 +28,7 @@ class SqliteStorage extends CapacitorStorage {
} }
function tableExists(string $tableName): bool { 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, # 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 # mais le nom sqlite_master est toujours valable pour le moment
"select name from sqlite_master ", "select name from sqlite_master ",
@ -39,7 +38,7 @@ class SqliteStorage extends CapacitorStorage {
} }
function channelExists(string $name): bool { function channelExists(string $name): bool {
$name = $this->db->get([ $name = $this->sqlite->get([
"select name from _channels", "select name from _channels",
"where" => ["name" => $name], "where" => ["name" => $name],
]); ]);
@ -47,11 +46,11 @@ class SqliteStorage extends CapacitorStorage {
} }
protected function _afterCreate(CapacitorChannel $channel): void { protected function _afterCreate(CapacitorChannel $channel): void {
$db = $this->db; $sqlite = $this->sqlite;
if (!$this->tableExists("_channels")) { if (!$this->tableExists("_channels")) {
# ne pas créer si la table existe déjà, pour éviter d'avoir besoin d'un # ne pas créer si la table existe déjà, pour éviter d'avoir besoin d'un
# verrou en écriture # verrou en écriture
$db->exec([ $sqlite->exec([
"create table if not exists", "create table if not exists",
"table" => "_channels", "table" => "_channels",
"cols" => [ "cols" => [
@ -64,7 +63,7 @@ class SqliteStorage extends CapacitorStorage {
if (!$this->channelExists($channel->getName())) { if (!$this->channelExists($channel->getName())) {
# ne pas insérer si la ligne existe déjà, pour éviter d'avoir besoin d'un # ne pas insérer si la ligne existe déjà, pour éviter d'avoir besoin d'un
# verrou en écriture # verrou en écriture
$db->exec([ $sqlite->exec([
"insert", "insert",
"into" => "_channels", "into" => "_channels",
"values" => [ "values" => [
@ -78,7 +77,7 @@ class SqliteStorage extends CapacitorStorage {
} }
protected function _beforeReset(CapacitorChannel $channel): void { protected function _beforeReset(CapacitorChannel $channel): void {
$this->db->exec([ $this->sqlite->exec([
"delete", "delete",
"from" => "_channels", "from" => "_channels",
"where" => [ "where" => [
@ -92,6 +91,6 @@ class SqliteStorage extends CapacitorStorage {
} }
function close(): void { function close(): void {
$this->db->close(); $this->sqlite->close();
} }
} }

View File

@ -19,8 +19,11 @@ class query extends _base {
const DEBUG_QUERIES = false; const DEBUG_QUERIES = false;
function useStmt(SQLite3 $db, ?SQLite3Stmt &$stmt=null, ?string &$sql=null): bool { function _use_stmt(SQLite3 $db, ?SQLite3Stmt &$stmt=null, ?string &$sql=null): bool {
if (static::DEBUG_QUERIES) msg::info($this->sql); #XXX if (static::DEBUG_QUERIES) {#XXX
msg::info($this->sql);
//msg::info(var_export($this->bindings, true));
}
if ($this->bindings !== null) { if ($this->bindings !== null) {
/** @var SQLite3Stmt $stmt */ /** @var SQLite3Stmt $stmt */
$stmt = SqliteException::check($db, $db->prepare($this->sql)); $stmt = SqliteException::check($db, $db->prepare($this->sql));

View File

@ -61,10 +61,7 @@ class func {
* la fonction (ne pas uniquement faire une vérification syntaxique) * la fonction (ne pas uniquement faire une vérification syntaxique)
*/ */
static function verifix_function(&$func, bool $strict=true, ?string &$reason=null): bool { static function verifix_function(&$func, bool $strict=true, ?string &$reason=null): bool {
if ($strict) { if ($strict) $reason = null;
$msg = var_export($func, true);
$reason = null;
}
if ($func instanceof ReflectionFunction) return true; if ($func instanceof ReflectionFunction) return true;
if (is_string($func)) { if (is_string($func)) {
$c = false; $c = false;
@ -85,11 +82,11 @@ class func {
if ($strict) { if ($strict) {
$reason = null; $reason = null;
if (class_exists($f)) { if (class_exists($f)) {
$reason = "$msg: is a class"; $reason = "$f: is a class";
return false; return false;
} }
if (!function_exists($f)) { if (!function_exists($f)) {
$reason = "$msg: function not found"; $reason = "$f: function not found";
return false; return false;
} }
} }
@ -120,10 +117,7 @@ class func {
* faire une vérification syntaxique) * faire une vérification syntaxique)
*/ */
static function verifix_class(&$func, bool $strict=true, ?string &$reason=null): bool { static function verifix_class(&$func, bool $strict=true, ?string &$reason=null): bool {
if ($strict) { if ($strict) $reason = null;
$msg = var_export($func, true);
$reason = null;
}
if ($func instanceof ReflectionClass) return true; if ($func instanceof ReflectionClass) return true;
if (is_string($func)) { if (is_string($func)) {
$c = $func; $c = $func;
@ -141,12 +135,10 @@ class func {
if (self::_parse_static($c)) return false; if (self::_parse_static($c)) return false;
if (self::_parse_method($c)) return false; if (self::_parse_method($c)) return false;
if ($f !== false) return false; if ($f !== false) return false;
if ($strict) { if ($strict && !class_exists($c)) {
if (!class_exists($c)) { $reason = "$c: class not found";
$reason = "$msg: class not found";
return false; return false;
} }
}
$func = [$c, false]; $func = [$c, false];
return true; return true;
} }
@ -210,10 +202,7 @@ class func {
* la méthode est liée (ne pas uniquement faire une vérification syntaxique) * 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 { static function verifix_static(&$func, bool $strict=true, ?bool &$bound=null, ?string &$reason=null): bool {
if ($strict) { if ($strict) $reason = null;
$msg = var_export($func, true);
$reason = null;
}
if ($func instanceof ReflectionMethod) { if ($func instanceof ReflectionMethod) {
$bound = false; $bound = false;
return true; return true;
@ -268,18 +257,19 @@ class func {
return false; return false;
} }
if ($strict) { if ($strict) {
[$c, $f] = $cf;
$reason = null; $reason = null;
if ($bound) { if ($bound) {
if (!class_exists($c)) { if (!class_exists($c)) {
$reason = "$msg: class not found"; $reason = "$c: class not found";
return false; return false;
} }
if (!method_exists($c, $f)) { if (!method_exists($c, $f)) {
$reason = "$msg: method not found"; $reason = "$c::$f: method not found";
return false; return false;
} }
} else { } else {
$reason = "$msg: not bound"; $reason = "$c::$f: not bound";
} }
} }
$func = $cf; $func = $cf;
@ -345,10 +335,7 @@ class func {
* la méthode est liée (ne pas uniquement faire une vérification syntaxique) * 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 { static function verifix_method(&$func, bool $strict=true, ?bool &$bound=null, ?string &$reason=null): bool {
if ($strict) { if ($strict) $reason = null;
$msg = var_export($func, true);
$reason = null;
}
if ($func instanceof ReflectionMethod) { if ($func instanceof ReflectionMethod) {
$bound = false; $bound = false;
return true; return true;
@ -404,18 +391,19 @@ class func {
return false; return false;
} }
if ($strict) { if ($strict) {
[$c, $f] = $cf;
$reason = null; $reason = null;
if ($bound) { if ($bound) {
if (!is_object($c) && !class_exists($c)) { if (!is_object($c) && !class_exists($c)) {
$reason = "$msg: class not found"; $reason = "$c: class not found";
return false; return false;
} }
if (!method_exists($c, $f)) { if (!method_exists($c, $f)) {
$reason = "$msg: method not found"; $reason = "$c::$f: method not found";
return false; return false;
} }
} else { } else {
$reason = "$msg: not bound"; $reason = "$c::$f: not bound";
} }
} }
$func = $cf; $func = $cf;

View File

@ -118,7 +118,7 @@ class SqliteStorageTest extends TestCase {
const COLUMN_DEFINITIONS = [ const COLUMN_DEFINITIONS = [
"a__" => "varchar", "a__" => "varchar",
"b__" => "varchar", "b__" => "varchar",
"b__sum_" => self::SUM_DEFINITION, "b__sum_" => "sersum",
]; ];
function getItemValues($item): ?array { function getItemValues($item): ?array {