modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2024-06-07 11:57:15 +04:00
parent f35b0a400d
commit 2fbe3ec09c
9 changed files with 137 additions and 68 deletions

View File

@ -11,6 +11,7 @@ use nur\sery\db\Capacitor;
use nur\sery\db\CapacitorChannel; use nur\sery\db\CapacitorChannel;
use nur\sery\db\mysql\MysqlStorage; use nur\sery\db\mysql\MysqlStorage;
use nur\sery\db\sqlite\SqliteStorage; use nur\sery\db\sqlite\SqliteStorage;
use nur\sery\file\Stream;
use nur\yaml; use nur\yaml;
Application::run(new class extends Application { Application::run(new class extends Application {
@ -68,12 +69,17 @@ Application::run(new class extends Application {
$args = $this->args; $args = $this->args;
if (!$args) { if (!$args) {
# lister les id # lister les id
$rows = $storage->mysql()->all([ $out = new Stream(STDOUT);
"select id_", $primaryKeys = $storage->getPrimaryKeys($channel);
$rows = $storage->db()->all([
"select",
"cols" => $primaryKeys,
"from" => $channel->getTableName(), "from" => $channel->getTableName(),
]); ]);
$out->fputcsv($primaryKeys);
foreach ($rows as $row) { foreach ($rows as $row) {
echo "$row[id_]\n"; $rowIds = $storage->getRowIds($channel, $row);
$out->fputcsv($rowIds);
} }
} else { } else {
# afficher les lignes correspondantes # afficher les lignes correspondantes

View File

@ -9,6 +9,7 @@ use nur\path;
use nur\sery\db\Capacitor; use nur\sery\db\Capacitor;
use nur\sery\db\CapacitorChannel; use nur\sery\db\CapacitorChannel;
use nur\sery\db\sqlite\SqliteStorage; use nur\sery\db\sqlite\SqliteStorage;
use nur\sery\file\Stream;
use nur\yaml; use nur\yaml;
Application::run(new class extends Application { Application::run(new class extends Application {
@ -64,12 +65,17 @@ Application::run(new class extends Application {
$args = $this->args; $args = $this->args;
if (!$args) { if (!$args) {
# lister les id # lister les id
$rows = $storage->sqlite()->all([ $out = new Stream(STDOUT);
"select id_", $primaryKeys = $storage->getPrimaryKeys($channel);
$rows = $storage->db()->all([
"select",
"cols" => $primaryKeys,
"from" => $channel->getTableName(), "from" => $channel->getTableName(),
]); ]);
$out->fputcsv($primaryKeys);
foreach ($rows as $row) { foreach ($rows as $row) {
echo "$row[id_]\n"; $rowIds = $storage->getRowIds($channel, $row);
$out->fputcsv($rowIds);
} }
} else { } else {
# afficher les lignes correspondantes # afficher les lignes correspondantes

View File

@ -153,7 +153,7 @@ class CapacitorChannel {
const SUM_DEFINITION = "varchar(40)"; const SUM_DEFINITION = "varchar(40)";
final function sum(string $string): string { final function sum(?string $string): string {
return sha1($string); return sha1($string);
} }

View File

@ -93,13 +93,13 @@ abstract class CapacitorStorage {
return $values; return $values;
} }
protected function getPrimaryKeys(CapacitorChannel $channel): array { function getPrimaryKeys(CapacitorChannel $channel): array {
$primaryKeys = $channel->getPrimaryKeys(); $primaryKeys = $channel->getPrimaryKeys();
if ($primaryKeys === null) $primaryKeys = ["id_"]; if ($primaryKeys === null) $primaryKeys = ["id_"];
return $primaryKeys; 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); $primaryKeys = $this->getPrimaryKeys($channel);
$rowIds = cl::select($row, $primaryKeys); $rowIds = cl::select($row, $primaryKeys);
if (cl::all_n($rowIds)) return null; if (cl::all_n($rowIds)) return null;

View File

@ -84,70 +84,88 @@ abstract class _base {
$condkey++; $condkey++;
} }
$op = strtolower($op); $op = strtolower($op);
$condvalues = null;
switch ($op) { switch ($op) {
case "between": case "between":
# ["between", $upper, $lower]
$condsep = " and "; $condsep = " and ";
$values = null;
if (array_key_exists("lower", $cond)) { if (array_key_exists("lower", $cond)) {
$values[] = $cond["lower"]; $condvalues[] = $cond["lower"];
} elseif (array_key_exists($condkey, $condkeys)) { } elseif (array_key_exists($condkey, $condkeys)) {
$values[] = $cond[$condkeys[$condkey]]; $condvalues[] = $cond[$condkeys[$condkey]];
$condkey++; $condkey++;
} }
if (array_key_exists("upper", $cond)) { if (array_key_exists("upper", $cond)) {
$values[] = $cond["upper"]; $condvalues[] = $cond["upper"];
} elseif (array_key_exists($condkey, $condkeys)) { } elseif (array_key_exists($condkey, $condkeys)) {
$values[] = $cond[$condkeys[$condkey]]; $condvalues[] = $cond[$condkeys[$condkey]];
$condkey++; $condkey++;
} }
break; break;
case "in": case "in":
# ["in", $values]
$condprefix = "("; $condprefix = "(";
$condsep = ", "; $condsep = ", ";
$condsuffix = ")"; $condsuffix = ")";
$values = null; $condvalues = null;
if (array_key_exists("values", $cond)) { if (array_key_exists("values", $cond)) {
$values = cl::with($cond["values"]); $condvalues = cl::with($cond["values"]);
} elseif (array_key_exists($condkey, $condkeys)) { } elseif (array_key_exists($condkey, $condkeys)) {
$values = cl::with($cond[$condkeys[$condkey]]); $condvalues = cl::with($cond[$condkeys[$condkey]]);
$condkey++; $condkey++;
} }
break; break;
case "null": case "null":
case "is null": case "is null":
$op = "is null"; $op = "is null";
$values = null;
break; break;
case "not null": case "not null":
case "is not null": case "is not null":
$op = "is not null"; $op = "is not null";
$values = null;
break; break;
default: default:
$values = null;
if (array_key_exists("value", $cond)) { if (array_key_exists("value", $cond)) {
$values = [$cond["value"]]; $condvalues = [$cond["value"]];
} elseif (array_key_exists($condkey, $condkeys)) { } elseif (array_key_exists($condkey, $condkeys)) {
$values = [$cond[$condkeys[$condkey]]]; $condvalues = [$cond[$condkeys[$condkey]]];
$condkey++; $condkey++;
} }
} }
} elseif ($cond !== null) { } elseif ($cond !== null) {
$op = "="; $op = "=";
$values = [$cond]; $condvalues = [$cond];
} else { } else {
$op = "is null"; $op = "is null";
$values = null; $condvalues = null;
} }
$cond = [$key, $op]; $cond = [$key, $op];
if ($values !== null) { if ($condvalues !== null) {
$parts = []; $parts = [];
foreach ($values as $value) { foreach ($condvalues as $condvalue) {
$param = "$key$i"; if (is_array($condvalue)) {
$parts[] = ":$param"; $first = true;
$bindings[$param] = $value; foreach ($condvalue as $value) {
if ($i === false) $i = 2; if ($first) {
else $i++; $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; $cond[] = $condprefix.implode($condsep, $parts).$condsuffix;
} }
@ -183,7 +201,7 @@ abstract class _base {
# paramètre # paramètre
$param = $key; $param = $key;
if ($bindings !== null && array_key_exists($param, $bindings)) { if ($bindings !== null && array_key_exists($param, $bindings)) {
$i = 1; $i = 2;
while (array_key_exists("$key$i", $bindings)) { while (array_key_exists("$key$i", $bindings)) {
$i++; $i++;
} }

View File

@ -12,14 +12,14 @@ use nur\sery\ValueException;
*/ */
class MysqlStorage extends CapacitorStorage { class MysqlStorage extends CapacitorStorage {
function __construct($mysql) { function __construct($mysql) {
$this->mysql = Mysql::with($mysql); $this->db = Mysql::with($mysql);
} }
/** @var Mysql */ /** @var Mysql */
protected $mysql; protected $db;
function mysql(): Mysql { function db(): Mysql {
return $this->mysql; return $this->db;
} }
const PRIMARY_KEY_DEFINITION = [ const PRIMARY_KEY_DEFINITION = [
@ -29,7 +29,7 @@ class MysqlStorage extends CapacitorStorage {
protected function _create(CapacitorChannel $channel): void { protected function _create(CapacitorChannel $channel): void {
if (!$channel->isCreated()) { if (!$channel->isCreated()) {
$cols = $this->ColumnDefinitions($channel); $cols = $this->ColumnDefinitions($channel);
$this->mysql->exec([ $this->db->exec([
"create table if not exists", "create table if not exists",
"table" => $channel->getTableName(), "table" => $channel->getTableName(),
"cols" => $cols, "cols" => $cols,
@ -39,7 +39,7 @@ class MysqlStorage extends CapacitorStorage {
} }
function _exists(CapacitorChannel $channel): bool { function _exists(CapacitorChannel $channel): bool {
$mysql = $this->mysql; $mysql = $this->db;
$tableName = $mysql->get([ $tableName = $mysql->get([
"select table_name from information_schema.tables", "select table_name from information_schema.tables",
"where" => [ "where" => [
@ -55,7 +55,7 @@ class MysqlStorage extends CapacitorStorage {
} }
function _reset(CapacitorChannel $channel): void { function _reset(CapacitorChannel $channel): void {
$this->mysql->exec([ $this->db->exec([
"drop table if exists", "drop table if exists",
$channel->getTableName(), $channel->getTableName(),
]); ]);
@ -73,7 +73,7 @@ class MysqlStorage extends CapacitorStorage {
$rowIds = $this->getRowIds($channel, $row, $primaryKeys); $rowIds = $this->getRowIds($channel, $row, $primaryKeys);
if ($rowIds !== null) { if ($rowIds !== null) {
# modification # modification
$prow = $this->mysql->one([ $prow = $this->db->one([
"select", "select",
"from" => $tableName, "from" => $tableName,
"where" => $rowIds, "where" => $rowIds,
@ -120,13 +120,13 @@ class MysqlStorage extends CapacitorStorage {
# aucune modification # aucune modification
return 0; return 0;
} elseif ($insert) { } elseif ($insert) {
$this->mysql->exec([ $this->db->exec([
"insert", "insert",
"into" => $tableName, "into" => $tableName,
"values" => $row, "values" => $row,
]); ]);
} else { } else {
$this->mysql->exec([ $this->db->exec([
"update", "update",
"table" => $tableName, "table" => $tableName,
"values" => $row, "values" => $row,
@ -137,7 +137,7 @@ class MysqlStorage extends CapacitorStorage {
} }
function _discharge(CapacitorChannel $channel, bool $reset=true): iterable { function _discharge(CapacitorChannel $channel, bool $reset=true): iterable {
$rows = $this->mysql->all([ $rows = $this->db->all([
"select item__", "select item__",
"from" => $channel->getTableName(), "from" => $channel->getTableName(),
]); ]);
@ -158,7 +158,7 @@ class MysqlStorage extends CapacitorStorage {
function _count(CapacitorChannel $channel, $filter): int { function _count(CapacitorChannel $channel, $filter): int {
$this->verifixFilter($channel, $filter); $this->verifixFilter($channel, $filter);
return $this->mysql->get([ return $this->db->get([
"select count(*)", "select count(*)",
"from" => $channel->getTableName(), "from" => $channel->getTableName(),
"where" => $filter, "where" => $filter,
@ -168,7 +168,7 @@ class MysqlStorage extends CapacitorStorage {
function _one(CapacitorChannel $channel, $filter): ?array { function _one(CapacitorChannel $channel, $filter): ?array {
if ($filter === null) throw ValueException::null("filter"); if ($filter === null) throw ValueException::null("filter");
$this->verifixFilter($channel, $filter); $this->verifixFilter($channel, $filter);
$row = $this->mysql->one([ $row = $this->db->one([
"select", "select",
"from" => $channel->getTableName(), "from" => $channel->getTableName(),
"where" => $filter, "where" => $filter,
@ -178,7 +178,7 @@ class MysqlStorage extends CapacitorStorage {
function _all(CapacitorChannel $channel, $filter): iterable { function _all(CapacitorChannel $channel, $filter): iterable {
$this->verifixFilter($channel, $filter); $this->verifixFilter($channel, $filter);
$rows = $this->mysql->all([ $rows = $this->db->all([
"select", "select",
"from" => $channel->getTableName(), "from" => $channel->getTableName(),
"where" => $filter, "where" => $filter,
@ -192,7 +192,7 @@ class MysqlStorage extends CapacitorStorage {
if ($func === null) $func = "->onEach"; if ($func === null) $func = "->onEach";
func::ensure_func($func, $channel, $args); func::ensure_func($func, $channel, $args);
$onEach = func::_prepare($func); $onEach = func::_prepare($func);
$mysql = $this->mysql; $mysql = $this->db;
$tableName = $channel->getTableName(); $tableName = $channel->getTableName();
$commited = false; $commited = false;
$count = 0; $count = 0;
@ -233,6 +233,6 @@ class MysqlStorage extends CapacitorStorage {
} }
function close(): void { function close(): void {
$this->mysql->close(); $this->db->close();
} }
} }

View File

@ -12,14 +12,14 @@ use nur\sery\ValueException;
*/ */
class SqliteStorage extends CapacitorStorage { class SqliteStorage extends CapacitorStorage {
function __construct($sqlite) { function __construct($sqlite) {
$this->sqlite = Sqlite::with($sqlite); $this->db = Sqlite::with($sqlite);
} }
/** @var Sqlite */ /** @var Sqlite */
protected $sqlite; protected $db;
function sqlite(): Sqlite { function db(): Sqlite {
return $this->sqlite; return $this->db;
} }
const PRIMARY_KEY_DEFINITION = [ const PRIMARY_KEY_DEFINITION = [
@ -29,7 +29,7 @@ class SqliteStorage extends CapacitorStorage {
protected function _create(CapacitorChannel $channel): void { protected function _create(CapacitorChannel $channel): void {
if (!$channel->isCreated()) { if (!$channel->isCreated()) {
$cols = $this->ColumnDefinitions($channel); $cols = $this->ColumnDefinitions($channel);
$this->sqlite->exec([ $this->db->exec([
"create table if not exists", "create table if not exists",
"table" => $channel->getTableName(), "table" => $channel->getTableName(),
"cols" => $cols, "cols" => $cols,
@ -39,7 +39,7 @@ class SqliteStorage extends CapacitorStorage {
} }
function _exists(CapacitorChannel $channel): bool { function _exists(CapacitorChannel $channel): bool {
$tableName = $this->sqlite->get([ $tableName = $this->db->get([
"select name from sqlite_schema", "select name from sqlite_schema",
"where" => [ "where" => [
"name" => $channel->getTableName(), "name" => $channel->getTableName(),
@ -53,7 +53,7 @@ class SqliteStorage extends CapacitorStorage {
} }
function _reset(CapacitorChannel $channel): void { function _reset(CapacitorChannel $channel): void {
$this->sqlite->exec([ $this->db->exec([
"drop table if exists", "drop table if exists",
$channel->getTableName(), $channel->getTableName(),
]); ]);
@ -71,7 +71,7 @@ class SqliteStorage extends CapacitorStorage {
$rowIds = $this->getRowIds($channel, $row, $primaryKeys); $rowIds = $this->getRowIds($channel, $row, $primaryKeys);
if ($rowIds !== null) { if ($rowIds !== null) {
# modification # modification
$prow = $this->sqlite->one([ $prow = $this->db->one([
"select", "select",
"from" => $tableName, "from" => $tableName,
"where" => $rowIds, "where" => $rowIds,
@ -118,13 +118,13 @@ class SqliteStorage extends CapacitorStorage {
# aucune modification # aucune modification
return 0; return 0;
} elseif ($insert) { } elseif ($insert) {
$this->sqlite->exec([ $this->db->exec([
"insert", "insert",
"into" => $tableName, "into" => $tableName,
"values" => $row, "values" => $row,
]); ]);
} else { } else {
$this->sqlite->exec([ $this->db->exec([
"update", "update",
"table" => $tableName, "table" => $tableName,
"values" => $row, "values" => $row,
@ -135,7 +135,7 @@ class SqliteStorage extends CapacitorStorage {
} }
function _discharge(CapacitorChannel $channel, bool $reset=true): iterable { function _discharge(CapacitorChannel $channel, bool $reset=true): iterable {
$rows = $this->sqlite->all([ $rows = $this->db->all([
"select item__", "select item__",
"from" => $channel->getTableName(), "from" => $channel->getTableName(),
]); ]);
@ -156,7 +156,7 @@ class SqliteStorage extends CapacitorStorage {
function _count(CapacitorChannel $channel, $filter): int { function _count(CapacitorChannel $channel, $filter): int {
$this->verifixFilter($channel, $filter); $this->verifixFilter($channel, $filter);
return $this->sqlite->get([ return $this->db->get([
"select count(*)", "select count(*)",
"from" => $channel->getTableName(), "from" => $channel->getTableName(),
"where" => $filter, "where" => $filter,
@ -166,7 +166,7 @@ class SqliteStorage extends CapacitorStorage {
function _one(CapacitorChannel $channel, $filter): ?array { function _one(CapacitorChannel $channel, $filter): ?array {
if ($filter === null) throw ValueException::null("filter"); if ($filter === null) throw ValueException::null("filter");
$this->verifixFilter($channel, $filter); $this->verifixFilter($channel, $filter);
$row = $this->sqlite->one([ $row = $this->db->one([
"select", "select",
"from" => $channel->getTableName(), "from" => $channel->getTableName(),
"where" => $filter, "where" => $filter,
@ -176,7 +176,7 @@ class SqliteStorage extends CapacitorStorage {
function _all(CapacitorChannel $channel, $filter): iterable { function _all(CapacitorChannel $channel, $filter): iterable {
$this->verifixFilter($channel, $filter); $this->verifixFilter($channel, $filter);
$rows = $this->sqlite->all([ $rows = $this->db->all([
"select", "select",
"from" => $channel->getTableName(), "from" => $channel->getTableName(),
"where" => $filter, "where" => $filter,
@ -190,7 +190,7 @@ class SqliteStorage extends CapacitorStorage {
if ($func === null) $func = "->onEach"; if ($func === null) $func = "->onEach";
func::ensure_func($func, $channel, $args); func::ensure_func($func, $channel, $args);
$onEach = func::_prepare($func); $onEach = func::_prepare($func);
$sqlite = $this->sqlite; $sqlite = $this->db;
$tableName = $channel->getTableName(); $tableName = $channel->getTableName();
$commited = false; $commited = false;
$count = 0; $count = 0;
@ -231,6 +231,6 @@ class SqliteStorage extends CapacitorStorage {
} }
function close(): void { function close(): void {
$this->sqlite->close(); $this->db->close();
} }
} }

View File

@ -16,6 +16,23 @@ use nur\sery\ValueException;
class Stream extends AbstractIterator implements IReader, IWriter { class Stream extends AbstractIterator implements IReader, IWriter {
use TStreamFilter; 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? */ /** @var bool les opérations de verrouillages sont-elle activées? */
const USE_LOCKING = false; const USE_LOCKING = false;
@ -153,6 +170,8 @@ class Stream extends AbstractIterator implements IReader, IWriter {
protected function getCsvParams($fd): array { protected function getCsvParams($fd): array {
$flavour = $this->csvFlavour; $flavour = $this->csvFlavour;
if ($flavour === null) { if ($flavour === null) {
self::probe_fd($fd, $seekable, $readable);
if (!$seekable || !$readable) $fd = null;
if ($fd === null) { if ($fd === null) {
# utiliser la valeur par défaut # utiliser la valeur par défaut
$flavour = static::DEFAULT_CSV_FLAVOUR; $flavour = static::DEFAULT_CSV_FLAVOUR;
@ -309,8 +328,8 @@ class Stream extends AbstractIterator implements IReader, IWriter {
} }
private function _rewindFd(): void { private function _rewindFd(): void {
$md = stream_get_meta_data($this->fd); self::probe_fd($this->fd, $seekable);
if ($md["seekable"]) $this->fseek(0); if ($seekable) $this->fseek(0);
} }
protected function _teardown(): void { protected function _teardown(): void {

View File

@ -64,6 +64,26 @@ class _queryTest extends TestCase {
_query_base::parse_conds(["col" => ["in", ["one", "two"]]], $sql, $params); _query_base::parse_conds(["col" => ["in", ["one", "two"]]], $sql, $params);
self::assertSame(["col in (:col, :col2)"], $sql); self::assertSame(["col in (:col, :col2)"], $sql);
self::assertSame(["col" => "one", "col2" => "two"], $params); 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 { function testParseValues(): void {
@ -99,7 +119,7 @@ class _queryTest extends TestCase {
$sql = $params = null; $sql = $params = null;
_query_base::parse_set_values([["int" => 42, "string" => "value"], ["int" => 24, "string" => "eulav"]], $sql, $params); _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 = :int", "string = :string", "int = :int2", "string = :string2"], $sql);
self::assertSame(["int" => 42, "string" => "value", "int1" => 24, "string1" => "eulav"], $params); self::assertSame(["int" => 42, "string" => "value", "int2" => 24, "string2" => "eulav"], $params);
} }
} }