implémenter group by et having

This commit is contained in:
Jephté Clain 2024-05-09 11:54:15 +04:00
parent 5e760ce71b
commit 86cab90788
3 changed files with 76 additions and 13 deletions

View File

@ -126,7 +126,7 @@ class _Query {
} }
} }
static function parse_values(?array $values, ?array &$sql, ?array &$params): void { static function parse_set_values(?array $values, ?array &$sql, ?array &$params): void {
if (!$values) return; if (!$values) return;
$index = 0; $index = 0;
$parts = []; $parts = [];
@ -135,7 +135,7 @@ class _Query {
## séquentiel ## séquentiel
if (is_array($part)) { if (is_array($part)) {
# paramètres récursifs # paramètres récursifs
self::parse_values($part, $parts, $params); self::parse_set_values($part, $parts, $params);
} else { } else {
# paramètre litéral # paramètre litéral
$parts[] = strval($part); $parts[] = strval($part);
@ -204,8 +204,10 @@ class _Query {
### vérifier la présence des parties nécessaires ### vérifier la présence des parties nécessaires
$sql = []; $sql = [];
if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix; if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix;
## select ## select
self::consume('select\s*', $tmpsql); $sql[] = "select"; self::consume('select\s*', $tmpsql); $sql[] = "select";
## cols ## cols
$usercols = []; $usercols = [];
if (self::consume('(.*?)\s*(?=$|\bfrom\b)', $tmpsql, $ms)) { if (self::consume('(.*?)\s*(?=$|\bfrom\b)', $tmpsql, $ms)) {
@ -235,6 +237,7 @@ class _Query {
} }
if (!$usercols && !$cols) $usercols = ["*"]; if (!$usercols && !$cols) $usercols = ["*"];
$sql[] = implode(" ", $usercols); $sql[] = implode(" ", $usercols);
## from ## from
$from = $query["from"] ?? null; $from = $query["from"] ?? null;
if (self::consume('from\s+([a-z_][a-z0-9_]*)\s*(?=;?\s*$|\bwhere\b)', $tmpsql, $ms)) { if (self::consume('from\s+([a-z_][a-z0-9_]*)\s*(?=;?\s*$|\bwhere\b)', $tmpsql, $ms)) {
@ -247,9 +250,10 @@ class _Query {
} else { } else {
throw new ValueException("expected table name: $usersql"); throw new ValueException("expected table name: $usersql");
} }
## where ## where
$userwhere = []; $userwhere = [];
if (self::consume('where\s*(.*?)(?=;?\s*$|\border\s+by\b)', $tmpsql, $ms)) { if (self::consume('where\b\s*(.*?)(?=;?\s*$|\border\s+by\b)', $tmpsql, $ms)) {
if ($ms[1]) $userwhere[] = $ms[1]; if ($ms[1]) $userwhere[] = $ms[1];
} }
$where = cl::withn($query["where"] ?? null); $where = cl::withn($query["where"] ?? null);
@ -258,9 +262,64 @@ class _Query {
$sql[] = "where"; $sql[] = "where";
$sql[] = implode(" and ", $userwhere); $sql[] = implode(" and ", $userwhere);
} }
# order by
# group by ## order by
# having $userorderby = [];
if (self::consume('order\s+by\b\s*(.*?)(?=;?\s*$|\bgroup\s+by\b)', $tmpsql, $ms)) {
if ($ms[1]) $userorderby[] = $ms[1];
}
$orderby = cl::withn($query["order by"] ?? null);
if ($orderby !== null) {
$index = 0;
foreach ($orderby as $key => $value) {
if ($key === $index) {
$userorderby[] = $value;
$index++;
} elseif ($value !== null) {
if (is_bool($value)) $value = $value? "asc": "desc";
$userorderby[] = "$key $value";
}
}
}
if ($userorderby) {
$sql[] = "order by";
$sql[] = implode(", ", $userorderby);
}
## group by
$usergroupby = [];
if (self::consume('group\s+by\b\s*(.*?)(?=;?\s*$|\bhaving\b)', $tmpsql, $ms)) {
if ($ms[1]) $usergroupby[] = $ms[1];
}
$groupby = cl::withn($query["group by"] ?? null);
if ($groupby !== null) {
$index = 0;
foreach ($groupby as $key => $value) {
if ($key === $index) {
$usergroupby[] = $value;
$index++;
} elseif ($value !== null) {
$usergroupby[] = $key;
}
}
}
if ($usergroupby) {
$sql[] = "group by";
$sql[] = implode(", ", $usergroupby);
}
## having
$userhaving = [];
if (self::consume('having\b\s*(.*?)(?=;?\s*$)', $tmpsql, $ms)) {
if ($ms[1]) $userhaving[] = $ms[1];
}
$having = cl::withn($query["having"] ?? null);
if ($having !== null) self::parse_conds($having, $userhaving, $params);
if ($userhaving) {
$sql[] = "having";
$sql[] = implode(" and ", $userhaving);
}
## fin de la requête
self::consume(';\s*', $tmpsql); self::consume(';\s*', $tmpsql);
if ($tmpsql) { if ($tmpsql) {
throw new ValueException("unexpected value at end: $usersql"); throw new ValueException("unexpected value at end: $usersql");

View File

@ -92,6 +92,10 @@ class SqliteTest extends TestCase {
"name" => "jclain1", "name" => "jclain1",
"amount" => 1, "amount" => 1,
], $sqlite->one(["select from user where name = 'jclain1'"])); ], $sqlite->one(["select from user where name = 'jclain1'"]));
self::assertSame([
"name" => "jclain1",
"amount" => 1,
], $sqlite->one(["select from user where", "where" => ["name = 'jclain1'"]]));
self::assertSame([ self::assertSame([
"name" => "jclain1", "name" => "jclain1",
"amount" => 1, "amount" => 1,

View File

@ -48,37 +48,37 @@ class _QueryTest extends TestCase {
function testParseValues(): void { function testParseValues(): void {
$sql = $params = null; $sql = $params = null;
_Query::parse_values(null, $sql, $params); _Query::parse_set_values(null, $sql, $params);
self::assertNull($sql); self::assertNull($sql);
self::assertNull($params); self::assertNull($params);
$sql = $params = null; $sql = $params = null;
_Query::parse_values([], $sql, $params); _Query::parse_set_values([], $sql, $params);
self::assertNull($sql); self::assertNull($sql);
self::assertNull($params); self::assertNull($params);
$sql = $params = null; $sql = $params = null;
_Query::parse_values(["col = 'value'"], $sql, $params); _Query::parse_set_values(["col = 'value'"], $sql, $params);
self::assertSame(["col = 'value'"], $sql); self::assertSame(["col = 'value'"], $sql);
self::assertNull($params); self::assertNull($params);
$sql = $params = null; $sql = $params = null;
_Query::parse_values([["col = 'value'"]], $sql, $params); _Query::parse_set_values([["col = 'value'"]], $sql, $params);
self::assertSame(["col = 'value'"], $sql); self::assertSame(["col = 'value'"], $sql);
self::assertNull($params); self::assertNull($params);
$sql = $params = null; $sql = $params = null;
_Query::parse_values(["int" => 42, "string" => "value"], $sql, $params); _Query::parse_set_values(["int" => 42, "string" => "value"], $sql, $params);
self::assertSame(["int = :int", "string = :string"], $sql); self::assertSame(["int = :int", "string = :string"], $sql);
self::assertSame(["int" => 42, "string" => "value"], $params); self::assertSame(["int" => 42, "string" => "value"], $params);
$sql = $params = null; $sql = $params = null;
_Query::parse_values(["int" => 42, "string" => "value"], $sql, $params); _Query::parse_set_values(["int" => 42, "string" => "value"], $sql, $params);
self::assertSame(["int = :int", "string = :string"], $sql); self::assertSame(["int = :int", "string = :string"], $sql);
self::assertSame(["int" => 42, "string" => "value"], $params); self::assertSame(["int" => 42, "string" => "value"], $params);
$sql = $params = null; $sql = $params = null;
_Query::parse_values([["int" => 42, "string" => "value"], ["int" => 24, "string" => "eulav"]], $sql, $params); _Query::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 = :int1", "string = :string1"], $sql);
self::assertSame(["int" => 42, "string" => "value", "int1" => 24, "string1" => "eulav"], $params); self::assertSame(["int" => 42, "string" => "value", "int1" => 24, "string1" => "eulav"], $params);
} }