From 2fbe3ec09cbab7ad33d1dbacfbfb5b82320b4716 Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Fri, 7 Jun 2024 11:57:15 +0400 Subject: [PATCH] modifs.mineures sans commentaires --- nur_bin/mysql-storage.php | 12 +++++-- nur_bin/sqlite-storage.php | 12 +++++-- src/db/CapacitorChannel.php | 2 +- src/db/CapacitorStorage.php | 4 +-- src/db/_private/_base.php | 64 +++++++++++++++++++++------------ src/db/mysql/MysqlStorage.php | 32 ++++++++--------- src/db/sqlite/SqliteStorage.php | 32 ++++++++--------- src/file/Stream.php | 23 ++++++++++-- tests/db/sqlite/_queryTest.php | 24 +++++++++++-- 9 files changed, 137 insertions(+), 68 deletions(-) diff --git a/nur_bin/mysql-storage.php b/nur_bin/mysql-storage.php index 0ea0ab1..bc0ba8b 100755 --- a/nur_bin/mysql-storage.php +++ b/nur_bin/mysql-storage.php @@ -11,6 +11,7 @@ use nur\sery\db\Capacitor; use nur\sery\db\CapacitorChannel; use nur\sery\db\mysql\MysqlStorage; use nur\sery\db\sqlite\SqliteStorage; +use nur\sery\file\Stream; use nur\yaml; Application::run(new class extends Application { @@ -68,12 +69,17 @@ Application::run(new class extends Application { $args = $this->args; if (!$args) { # lister les id - $rows = $storage->mysql()->all([ - "select id_", + $out = new Stream(STDOUT); + $primaryKeys = $storage->getPrimaryKeys($channel); + $rows = $storage->db()->all([ + "select", + "cols" => $primaryKeys, "from" => $channel->getTableName(), ]); + $out->fputcsv($primaryKeys); foreach ($rows as $row) { - echo "$row[id_]\n"; + $rowIds = $storage->getRowIds($channel, $row); + $out->fputcsv($rowIds); } } else { # afficher les lignes correspondantes diff --git a/nur_bin/sqlite-storage.php b/nur_bin/sqlite-storage.php index 2a5464e..095a2f2 100755 --- a/nur_bin/sqlite-storage.php +++ b/nur_bin/sqlite-storage.php @@ -9,6 +9,7 @@ use nur\path; use nur\sery\db\Capacitor; use nur\sery\db\CapacitorChannel; use nur\sery\db\sqlite\SqliteStorage; +use nur\sery\file\Stream; use nur\yaml; Application::run(new class extends Application { @@ -64,12 +65,17 @@ Application::run(new class extends Application { $args = $this->args; if (!$args) { # lister les id - $rows = $storage->sqlite()->all([ - "select id_", + $out = new Stream(STDOUT); + $primaryKeys = $storage->getPrimaryKeys($channel); + $rows = $storage->db()->all([ + "select", + "cols" => $primaryKeys, "from" => $channel->getTableName(), ]); + $out->fputcsv($primaryKeys); foreach ($rows as $row) { - echo "$row[id_]\n"; + $rowIds = $storage->getRowIds($channel, $row); + $out->fputcsv($rowIds); } } else { # afficher les lignes correspondantes diff --git a/src/db/CapacitorChannel.php b/src/db/CapacitorChannel.php index 3587c75..8bf81f6 100644 --- a/src/db/CapacitorChannel.php +++ b/src/db/CapacitorChannel.php @@ -153,7 +153,7 @@ class CapacitorChannel { const SUM_DEFINITION = "varchar(40)"; - final function sum(string $string): string { + final function sum(?string $string): string { return sha1($string); } diff --git a/src/db/CapacitorStorage.php b/src/db/CapacitorStorage.php index 9b79041..88d6465 100644 --- a/src/db/CapacitorStorage.php +++ b/src/db/CapacitorStorage.php @@ -93,13 +93,13 @@ abstract class CapacitorStorage { return $values; } - protected function getPrimaryKeys(CapacitorChannel $channel): array { + function getPrimaryKeys(CapacitorChannel $channel): array { $primaryKeys = $channel->getPrimaryKeys(); if ($primaryKeys === null) $primaryKeys = ["id_"]; return $primaryKeys; } - protected function getRowIds(CapacitorChannel $channel, ?array $row, ?array &$primaryKeys=null): ?array { + function getRowIds(CapacitorChannel $channel, ?array $row, ?array &$primaryKeys=null): ?array { $primaryKeys = $this->getPrimaryKeys($channel); $rowIds = cl::select($row, $primaryKeys); if (cl::all_n($rowIds)) return null; diff --git a/src/db/_private/_base.php b/src/db/_private/_base.php index 7f10ace..df634a1 100644 --- a/src/db/_private/_base.php +++ b/src/db/_private/_base.php @@ -84,70 +84,88 @@ abstract class _base { $condkey++; } $op = strtolower($op); + $condvalues = null; switch ($op) { case "between": + # ["between", $upper, $lower] $condsep = " and "; - $values = null; if (array_key_exists("lower", $cond)) { - $values[] = $cond["lower"]; + $condvalues[] = $cond["lower"]; } elseif (array_key_exists($condkey, $condkeys)) { - $values[] = $cond[$condkeys[$condkey]]; + $condvalues[] = $cond[$condkeys[$condkey]]; $condkey++; } if (array_key_exists("upper", $cond)) { - $values[] = $cond["upper"]; + $condvalues[] = $cond["upper"]; } elseif (array_key_exists($condkey, $condkeys)) { - $values[] = $cond[$condkeys[$condkey]]; + $condvalues[] = $cond[$condkeys[$condkey]]; $condkey++; } break; case "in": + # ["in", $values] $condprefix = "("; $condsep = ", "; $condsuffix = ")"; - $values = null; + $condvalues = null; if (array_key_exists("values", $cond)) { - $values = cl::with($cond["values"]); + $condvalues = cl::with($cond["values"]); } elseif (array_key_exists($condkey, $condkeys)) { - $values = cl::with($cond[$condkeys[$condkey]]); + $condvalues = cl::with($cond[$condkeys[$condkey]]); $condkey++; } break; case "null": case "is null": $op = "is null"; - $values = null; break; case "not null": case "is not null": $op = "is not null"; - $values = null; break; default: - $values = null; if (array_key_exists("value", $cond)) { - $values = [$cond["value"]]; + $condvalues = [$cond["value"]]; } elseif (array_key_exists($condkey, $condkeys)) { - $values = [$cond[$condkeys[$condkey]]]; + $condvalues = [$cond[$condkeys[$condkey]]]; $condkey++; } } } elseif ($cond !== null) { $op = "="; - $values = [$cond]; + $condvalues = [$cond]; } else { $op = "is null"; - $values = null; + $condvalues = null; } $cond = [$key, $op]; - if ($values !== null) { + if ($condvalues !== null) { $parts = []; - foreach ($values as $value) { - $param = "$key$i"; - $parts[] = ":$param"; - $bindings[$param] = $value; - if ($i === false) $i = 2; - else $i++; + foreach ($condvalues as $condvalue) { + if (is_array($condvalue)) { + $first = true; + foreach ($condvalue as $value) { + if ($first) { + $first = false; + } else { + if ($sep === null) $sep = "and"; + $parts[] = " $sep "; + $parts[] = $key; + $parts[] = " $op "; + } + $param = "$key$i"; + $parts[] = ":$param"; + $bindings[$param] = $value; + if ($i === false) $i = 2; + else $i++; + } + } else { + $param = "$key$i"; + $parts[] = ":$param"; + $bindings[$param] = $condvalue; + if ($i === false) $i = 2; + else $i++; + } } $cond[] = $condprefix.implode($condsep, $parts).$condsuffix; } @@ -183,7 +201,7 @@ abstract class _base { # paramètre $param = $key; if ($bindings !== null && array_key_exists($param, $bindings)) { - $i = 1; + $i = 2; while (array_key_exists("$key$i", $bindings)) { $i++; } diff --git a/src/db/mysql/MysqlStorage.php b/src/db/mysql/MysqlStorage.php index deae60e..cf8df02 100644 --- a/src/db/mysql/MysqlStorage.php +++ b/src/db/mysql/MysqlStorage.php @@ -12,14 +12,14 @@ use nur\sery\ValueException; */ class MysqlStorage extends CapacitorStorage { function __construct($mysql) { - $this->mysql = Mysql::with($mysql); + $this->db = Mysql::with($mysql); } /** @var Mysql */ - protected $mysql; + protected $db; - function mysql(): Mysql { - return $this->mysql; + function db(): Mysql { + return $this->db; } const PRIMARY_KEY_DEFINITION = [ @@ -29,7 +29,7 @@ class MysqlStorage extends CapacitorStorage { protected function _create(CapacitorChannel $channel): void { if (!$channel->isCreated()) { $cols = $this->ColumnDefinitions($channel); - $this->mysql->exec([ + $this->db->exec([ "create table if not exists", "table" => $channel->getTableName(), "cols" => $cols, @@ -39,7 +39,7 @@ class MysqlStorage extends CapacitorStorage { } function _exists(CapacitorChannel $channel): bool { - $mysql = $this->mysql; + $mysql = $this->db; $tableName = $mysql->get([ "select table_name from information_schema.tables", "where" => [ @@ -55,7 +55,7 @@ class MysqlStorage extends CapacitorStorage { } function _reset(CapacitorChannel $channel): void { - $this->mysql->exec([ + $this->db->exec([ "drop table if exists", $channel->getTableName(), ]); @@ -73,7 +73,7 @@ class MysqlStorage extends CapacitorStorage { $rowIds = $this->getRowIds($channel, $row, $primaryKeys); if ($rowIds !== null) { # modification - $prow = $this->mysql->one([ + $prow = $this->db->one([ "select", "from" => $tableName, "where" => $rowIds, @@ -120,13 +120,13 @@ class MysqlStorage extends CapacitorStorage { # aucune modification return 0; } elseif ($insert) { - $this->mysql->exec([ + $this->db->exec([ "insert", "into" => $tableName, "values" => $row, ]); } else { - $this->mysql->exec([ + $this->db->exec([ "update", "table" => $tableName, "values" => $row, @@ -137,7 +137,7 @@ class MysqlStorage extends CapacitorStorage { } function _discharge(CapacitorChannel $channel, bool $reset=true): iterable { - $rows = $this->mysql->all([ + $rows = $this->db->all([ "select item__", "from" => $channel->getTableName(), ]); @@ -158,7 +158,7 @@ class MysqlStorage extends CapacitorStorage { function _count(CapacitorChannel $channel, $filter): int { $this->verifixFilter($channel, $filter); - return $this->mysql->get([ + return $this->db->get([ "select count(*)", "from" => $channel->getTableName(), "where" => $filter, @@ -168,7 +168,7 @@ class MysqlStorage extends CapacitorStorage { function _one(CapacitorChannel $channel, $filter): ?array { if ($filter === null) throw ValueException::null("filter"); $this->verifixFilter($channel, $filter); - $row = $this->mysql->one([ + $row = $this->db->one([ "select", "from" => $channel->getTableName(), "where" => $filter, @@ -178,7 +178,7 @@ class MysqlStorage extends CapacitorStorage { function _all(CapacitorChannel $channel, $filter): iterable { $this->verifixFilter($channel, $filter); - $rows = $this->mysql->all([ + $rows = $this->db->all([ "select", "from" => $channel->getTableName(), "where" => $filter, @@ -192,7 +192,7 @@ class MysqlStorage extends CapacitorStorage { if ($func === null) $func = "->onEach"; func::ensure_func($func, $channel, $args); $onEach = func::_prepare($func); - $mysql = $this->mysql; + $mysql = $this->db; $tableName = $channel->getTableName(); $commited = false; $count = 0; @@ -233,6 +233,6 @@ class MysqlStorage extends CapacitorStorage { } function close(): void { - $this->mysql->close(); + $this->db->close(); } } diff --git a/src/db/sqlite/SqliteStorage.php b/src/db/sqlite/SqliteStorage.php index 7bfda95..6c69c4a 100644 --- a/src/db/sqlite/SqliteStorage.php +++ b/src/db/sqlite/SqliteStorage.php @@ -12,14 +12,14 @@ use nur\sery\ValueException; */ class SqliteStorage extends CapacitorStorage { function __construct($sqlite) { - $this->sqlite = Sqlite::with($sqlite); + $this->db = Sqlite::with($sqlite); } /** @var Sqlite */ - protected $sqlite; + protected $db; - function sqlite(): Sqlite { - return $this->sqlite; + function db(): Sqlite { + return $this->db; } const PRIMARY_KEY_DEFINITION = [ @@ -29,7 +29,7 @@ class SqliteStorage extends CapacitorStorage { protected function _create(CapacitorChannel $channel): void { if (!$channel->isCreated()) { $cols = $this->ColumnDefinitions($channel); - $this->sqlite->exec([ + $this->db->exec([ "create table if not exists", "table" => $channel->getTableName(), "cols" => $cols, @@ -39,7 +39,7 @@ class SqliteStorage extends CapacitorStorage { } function _exists(CapacitorChannel $channel): bool { - $tableName = $this->sqlite->get([ + $tableName = $this->db->get([ "select name from sqlite_schema", "where" => [ "name" => $channel->getTableName(), @@ -53,7 +53,7 @@ class SqliteStorage extends CapacitorStorage { } function _reset(CapacitorChannel $channel): void { - $this->sqlite->exec([ + $this->db->exec([ "drop table if exists", $channel->getTableName(), ]); @@ -71,7 +71,7 @@ class SqliteStorage extends CapacitorStorage { $rowIds = $this->getRowIds($channel, $row, $primaryKeys); if ($rowIds !== null) { # modification - $prow = $this->sqlite->one([ + $prow = $this->db->one([ "select", "from" => $tableName, "where" => $rowIds, @@ -118,13 +118,13 @@ class SqliteStorage extends CapacitorStorage { # aucune modification return 0; } elseif ($insert) { - $this->sqlite->exec([ + $this->db->exec([ "insert", "into" => $tableName, "values" => $row, ]); } else { - $this->sqlite->exec([ + $this->db->exec([ "update", "table" => $tableName, "values" => $row, @@ -135,7 +135,7 @@ class SqliteStorage extends CapacitorStorage { } function _discharge(CapacitorChannel $channel, bool $reset=true): iterable { - $rows = $this->sqlite->all([ + $rows = $this->db->all([ "select item__", "from" => $channel->getTableName(), ]); @@ -156,7 +156,7 @@ class SqliteStorage extends CapacitorStorage { function _count(CapacitorChannel $channel, $filter): int { $this->verifixFilter($channel, $filter); - return $this->sqlite->get([ + return $this->db->get([ "select count(*)", "from" => $channel->getTableName(), "where" => $filter, @@ -166,7 +166,7 @@ class SqliteStorage extends CapacitorStorage { function _one(CapacitorChannel $channel, $filter): ?array { if ($filter === null) throw ValueException::null("filter"); $this->verifixFilter($channel, $filter); - $row = $this->sqlite->one([ + $row = $this->db->one([ "select", "from" => $channel->getTableName(), "where" => $filter, @@ -176,7 +176,7 @@ class SqliteStorage extends CapacitorStorage { function _all(CapacitorChannel $channel, $filter): iterable { $this->verifixFilter($channel, $filter); - $rows = $this->sqlite->all([ + $rows = $this->db->all([ "select", "from" => $channel->getTableName(), "where" => $filter, @@ -190,7 +190,7 @@ class SqliteStorage extends CapacitorStorage { if ($func === null) $func = "->onEach"; func::ensure_func($func, $channel, $args); $onEach = func::_prepare($func); - $sqlite = $this->sqlite; + $sqlite = $this->db; $tableName = $channel->getTableName(); $commited = false; $count = 0; @@ -231,6 +231,6 @@ class SqliteStorage extends CapacitorStorage { } function close(): void { - $this->sqlite->close(); + $this->db->close(); } } diff --git a/src/file/Stream.php b/src/file/Stream.php index ce02919..6d3cf6a 100644 --- a/src/file/Stream.php +++ b/src/file/Stream.php @@ -16,6 +16,23 @@ use nur\sery\ValueException; class Stream extends AbstractIterator implements IReader, IWriter { use TStreamFilter; + protected static function probe_fd($fd, ?bool &$seekable=null, ?bool &$readable=null): void { + $md = stream_get_meta_data($fd); + $seekable = $md["seekable"]; + $mode = $md["mode"]; + $readable = strpos($mode, "r") !== false || strpos($mode, "+") !== false; + } + + protected static function fd_is_seekable($fd): bool { + self::probe_fd($fd, $seekable); + return $seekable; + } + + protected static function fd_is_readable($fd): bool { + $mode = stream_get_meta_data($fd)["mode"]; + return strpos($mode, "r") !== false || strpos($mode, "+") !== false; + } + /** @var bool les opérations de verrouillages sont-elle activées? */ const USE_LOCKING = false; @@ -153,6 +170,8 @@ class Stream extends AbstractIterator implements IReader, IWriter { protected function getCsvParams($fd): array { $flavour = $this->csvFlavour; if ($flavour === null) { + self::probe_fd($fd, $seekable, $readable); + if (!$seekable || !$readable) $fd = null; if ($fd === null) { # utiliser la valeur par défaut $flavour = static::DEFAULT_CSV_FLAVOUR; @@ -309,8 +328,8 @@ class Stream extends AbstractIterator implements IReader, IWriter { } private function _rewindFd(): void { - $md = stream_get_meta_data($this->fd); - if ($md["seekable"]) $this->fseek(0); + self::probe_fd($this->fd, $seekable); + if ($seekable) $this->fseek(0); } protected function _teardown(): void { diff --git a/tests/db/sqlite/_queryTest.php b/tests/db/sqlite/_queryTest.php index 32cc662..2fd73e5 100644 --- a/tests/db/sqlite/_queryTest.php +++ b/tests/db/sqlite/_queryTest.php @@ -64,6 +64,26 @@ class _queryTest extends TestCase { _query_base::parse_conds(["col" => ["in", ["one", "two"]]], $sql, $params); self::assertSame(["col in (:col, :col2)"], $sql); self::assertSame(["col" => "one", "col2" => "two"], $params); + + $sql = $params = null; + _query_base::parse_conds(["col" => ["=", ["one", "two"]]], $sql, $params); + self::assertSame(["col = :col and col = :col2"], $sql); + self::assertSame(["col" => "one", "col2" => "two"], $params); + + $sql = $params = null; + _query_base::parse_conds(["or", "col" => ["=", ["one", "two"]]], $sql, $params); + self::assertSame(["col = :col or col = :col2"], $sql); + self::assertSame(["col" => "one", "col2" => "two"], $params); + + $sql = $params = null; + _query_base::parse_conds(["col" => ["<>", ["one", "two"]]], $sql, $params); + self::assertSame(["col <> :col and col <> :col2"], $sql); + self::assertSame(["col" => "one", "col2" => "two"], $params); + + $sql = $params = null; + _query_base::parse_conds(["or", "col" => ["<>", ["one", "two"]]], $sql, $params); + self::assertSame(["col <> :col or col <> :col2"], $sql); + self::assertSame(["col" => "one", "col2" => "two"], $params); } function testParseValues(): void { @@ -99,7 +119,7 @@ class _queryTest extends TestCase { $sql = $params = null; _query_base::parse_set_values([["int" => 42, "string" => "value"], ["int" => 24, "string" => "eulav"]], $sql, $params); - self::assertSame(["int = :int", "string = :string", "int = :int1", "string = :string1"], $sql); - self::assertSame(["int" => 42, "string" => "value", "int1" => 24, "string1" => "eulav"], $params); + self::assertSame(["int = :int", "string = :string", "int = :int2", "string = :string2"], $sql); + self::assertSame(["int" => 42, "string" => "value", "int2" => 24, "string2" => "eulav"], $params); } }