implémentation de base de select
This commit is contained in:
parent
ea2449b766
commit
5e760ce71b
|
@ -1,5 +0,0 @@
|
||||||
<?php
|
|
||||||
namespace nur\sery\db;
|
|
||||||
|
|
||||||
class filters {
|
|
||||||
}
|
|
|
@ -141,6 +141,10 @@ class Sqlite {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function one($query, ?array $params=null): ?array {
|
||||||
|
return $this->get($query, $params, true);
|
||||||
|
}
|
||||||
|
|
||||||
protected function _fetchResult(SQLite3Result $result): Generator {
|
protected function _fetchResult(SQLite3Result $result): Generator {
|
||||||
try {
|
try {
|
||||||
while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) {
|
while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) {
|
||||||
|
|
|
@ -53,6 +53,120 @@ class _Query {
|
||||||
return $sql;
|
return $sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function is_sep(&$cond): bool {
|
||||||
|
if (!is_string($cond)) return false;
|
||||||
|
if (!preg_match('/^\s*(and|or|not)\s*$/i', $cond, $ms)) return false;
|
||||||
|
$cond = $ms[1];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function parse_conds(?array $conds, ?array &$sql, ?array &$params): void {
|
||||||
|
if (!$conds) return;
|
||||||
|
$sep = null;
|
||||||
|
$index = 0;
|
||||||
|
$condsql = [];
|
||||||
|
foreach ($conds as $key => $cond) {
|
||||||
|
if ($key === $index) {
|
||||||
|
## séquentiel
|
||||||
|
if ($index === 0 && self::is_sep($cond)) {
|
||||||
|
$sep = $cond;
|
||||||
|
} elseif (is_array($cond)) {
|
||||||
|
# condition récursive
|
||||||
|
self::parse_conds($cond, $condsql, $params);
|
||||||
|
} else {
|
||||||
|
# condition litérale
|
||||||
|
$condsql[] = strval($cond);
|
||||||
|
}
|
||||||
|
$index++;
|
||||||
|
} else {
|
||||||
|
## associatif
|
||||||
|
# paramètre
|
||||||
|
$param = $key;
|
||||||
|
if ($params !== null && array_key_exists($param, $params)) {
|
||||||
|
$i = 1;
|
||||||
|
while (array_key_exists("$key$i", $params)) {
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
$param = "$key$i";
|
||||||
|
}
|
||||||
|
# value ou [operator, value]
|
||||||
|
if (is_array($cond)) {
|
||||||
|
$op = null;
|
||||||
|
$value = null;
|
||||||
|
$condkeys = array_keys($cond);
|
||||||
|
if (array_key_exists("op", $cond)) $op = $cond["op"];
|
||||||
|
if (array_key_exists("value", $cond)) $value = $cond["value"];
|
||||||
|
$condkey = 0;
|
||||||
|
if ($op === null && array_key_exists($condkey, $condkeys)) {
|
||||||
|
$op = $cond[$condkeys[$condkey]];
|
||||||
|
$condkey++;
|
||||||
|
}
|
||||||
|
if ($value === null && array_key_exists($condkey, $condkeys)) {
|
||||||
|
$value = $cond[$condkeys[$condkey]];
|
||||||
|
$condkey++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$op = "=";
|
||||||
|
$value = $cond;
|
||||||
|
}
|
||||||
|
$cond = [$key, $op];
|
||||||
|
if ($value !== null) {
|
||||||
|
$cond[] = ":$param";
|
||||||
|
$params[$param] = $value;
|
||||||
|
}
|
||||||
|
$condsql[] = implode(" ", $cond);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($sep === null) $sep = "and";
|
||||||
|
$count = count($condsql);
|
||||||
|
if ($count > 1) {
|
||||||
|
$sql[] = "(" . implode(" $sep ", $condsql) . ")";
|
||||||
|
} elseif ($count == 1) {
|
||||||
|
$sql[] = $condsql[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function parse_values(?array $values, ?array &$sql, ?array &$params): void {
|
||||||
|
if (!$values) return;
|
||||||
|
$index = 0;
|
||||||
|
$parts = [];
|
||||||
|
foreach ($values as $key => $part) {
|
||||||
|
if ($key === $index) {
|
||||||
|
## séquentiel
|
||||||
|
if (is_array($part)) {
|
||||||
|
# paramètres récursifs
|
||||||
|
self::parse_values($part, $parts, $params);
|
||||||
|
} else {
|
||||||
|
# paramètre litéral
|
||||||
|
$parts[] = strval($part);
|
||||||
|
}
|
||||||
|
$index++;
|
||||||
|
} else {
|
||||||
|
## associatif
|
||||||
|
# paramètre
|
||||||
|
$param = $key;
|
||||||
|
if ($params !== null && array_key_exists($param, $params)) {
|
||||||
|
$i = 1;
|
||||||
|
while (array_key_exists("$key$i", $params)) {
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
$param = "$key$i";
|
||||||
|
}
|
||||||
|
# value
|
||||||
|
$value = $part;
|
||||||
|
$part = [$key, "="];
|
||||||
|
if ($value === null) {
|
||||||
|
$part[] = "null";
|
||||||
|
} else {
|
||||||
|
$part[] = ":$param";
|
||||||
|
$params[$param] = $value;
|
||||||
|
}
|
||||||
|
$parts[] = implode(" ", $part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$sql = cl::merge($sql, $parts);
|
||||||
|
}
|
||||||
|
|
||||||
const create_SCHEMA = [
|
const create_SCHEMA = [
|
||||||
"prefix" => "string",
|
"prefix" => "string",
|
||||||
"table" => "string",
|
"table" => "string",
|
||||||
|
@ -77,7 +191,7 @@ class _Query {
|
||||||
"having" => "?array",
|
"having" => "?array",
|
||||||
];
|
];
|
||||||
static function is_select(string $sql): bool {
|
static function is_select(string $sql): bool {
|
||||||
return false;
|
return preg_match("/^select\b/i", $sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,14 +201,14 @@ class _Query {
|
||||||
static function parse_select(array $query, ?array &$params=null): string {
|
static function parse_select(array $query, ?array &$params=null): string {
|
||||||
# fusionner d'abord toutes les parties séquentielles
|
# fusionner d'abord toutes les parties séquentielles
|
||||||
$usersql = $tmpsql = self::merge_seq($query);
|
$usersql = $tmpsql = self::merge_seq($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)) {
|
||||||
if ($ms[1]) $usercols[] = $ms[1];
|
if ($ms[1]) $usercols[] = $ms[1];
|
||||||
}
|
}
|
||||||
$tmpcols = cl::withn($query["cols"] ?? null);
|
$tmpcols = cl::withn($query["cols"] ?? null);
|
||||||
|
@ -119,24 +233,31 @@ class _Query {
|
||||||
$usercols = array_merge($usercols, $cols);
|
$usercols = array_merge($usercols, $cols);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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)) {
|
||||||
if ($from === null) $from = $ms[1];
|
if ($from === null) $from = $ms[1];
|
||||||
|
$sql[] = "from";
|
||||||
$sql[] = $from;
|
$sql[] = $from;
|
||||||
} elseif ($from !== null) {
|
} elseif ($from !== null) {
|
||||||
|
$sql[] = "from";
|
||||||
$sql[] = $from;
|
$sql[] = $from;
|
||||||
} 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\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);
|
||||||
|
if ($where !== null) self::parse_conds($where, $userwhere, $params);
|
||||||
|
if ($userwhere) {
|
||||||
|
$sql[] = "where";
|
||||||
|
$sql[] = implode(" and ", $userwhere);
|
||||||
|
}
|
||||||
# order by
|
# order by
|
||||||
# group by
|
# group by
|
||||||
# having
|
# having
|
||||||
|
|
|
@ -3,7 +3,6 @@ namespace nur\sery\db\sqlite;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use nulib\tests\TestCase;
|
use nulib\tests\TestCase;
|
||||||
use nur\sery\ValueException;
|
|
||||||
|
|
||||||
class SqliteTest extends TestCase {
|
class SqliteTest extends TestCase {
|
||||||
const CREATE_PERSON = "create table person(nom varchar, prenom varchar, age integer)";
|
const CREATE_PERSON = "create table person(nom varchar, prenom varchar, age integer)";
|
||||||
|
@ -70,4 +69,52 @@ class SqliteTest extends TestCase {
|
||||||
["i" => 10, "s" => null/*"dix"*/],
|
["i" => 10, "s" => null/*"dix"*/],
|
||||||
], iterator_to_array($sqlite->all("select * from mapping")));
|
], iterator_to_array($sqlite->all("select * from mapping")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testSelect() {
|
||||||
|
$sqlite = new Sqlite(":memory:", [
|
||||||
|
"migrate" => "create table user (name varchar, amount integer)",
|
||||||
|
]);
|
||||||
|
$sqlite->exec(["insert into user", "values" => ["name" => "jclain1", "amount" => 1]]);
|
||||||
|
$sqlite->exec(["insert into user", "values" => ["name" => "jclain2", "amount" => 2]]);
|
||||||
|
$sqlite->exec(["insert into user", "values" => ["name" => "jclain5", "amount" => 5]]);
|
||||||
|
$sqlite->exec(["insert into user", "values" => ["name" => "fclain7", "amount" => 7]]);
|
||||||
|
$sqlite->exec(["insert into user", "values" => ["name" => "fclain9", "amount" => 9]]);
|
||||||
|
$sqlite->exec(["insert into user", "values" => ["name" => "fclain10", "amount" => 10]]);
|
||||||
|
self::assertSame([
|
||||||
|
"name" => "jclain1",
|
||||||
|
"amount" => 1,
|
||||||
|
], $sqlite->one("select * from user where name = 'jclain1'"));
|
||||||
|
self::assertSame([
|
||||||
|
"name" => "jclain1",
|
||||||
|
"amount" => 1,
|
||||||
|
], $sqlite->one(["select * from user where name = 'jclain1'"]));
|
||||||
|
self::assertSame([
|
||||||
|
"name" => "jclain1",
|
||||||
|
"amount" => 1,
|
||||||
|
], $sqlite->one(["select from user where name = 'jclain1'"]));
|
||||||
|
self::assertSame([
|
||||||
|
"name" => "jclain1",
|
||||||
|
"amount" => 1,
|
||||||
|
], $sqlite->one(["select from user", "where" => ["name = 'jclain1'"]]));
|
||||||
|
self::assertSame([
|
||||||
|
"name" => "jclain1",
|
||||||
|
"amount" => 1,
|
||||||
|
], $sqlite->one(["select", "from" => "user", "where" => ["name = 'jclain1'"]]));
|
||||||
|
self::assertSame([
|
||||||
|
"name" => "jclain1",
|
||||||
|
"amount" => 1,
|
||||||
|
], $sqlite->one(["select", "from" => "user", "where" => ["name" => "jclain1"]]));
|
||||||
|
self::assertSame([
|
||||||
|
"name" => "jclain1",
|
||||||
|
], $sqlite->one(["select name", "from" => "user", "where" => ["name" => "jclain1"]]));
|
||||||
|
self::assertSame([
|
||||||
|
"name" => "jclain1",
|
||||||
|
], $sqlite->one(["select", "cols" => "name", "from" => "user", "where" => ["name" => "jclain1"]]));
|
||||||
|
self::assertSame([
|
||||||
|
"name" => "jclain1",
|
||||||
|
], $sqlite->one(["select", "cols" => ["name"], "from" => "user", "where" => ["name" => "jclain1"]]));
|
||||||
|
self::assertSame([
|
||||||
|
"plouf" => "jclain1",
|
||||||
|
], $sqlite->one(["select", "cols" => ["plouf" => "name"], "from" => "user", "where" => ["name" => "jclain1"]]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
<?php
|
||||||
|
namespace nur\sery\db\sqlite;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class _QueryTest extends TestCase {
|
||||||
|
function testParseConds(): void {
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_conds(null, $sql, $params);
|
||||||
|
self::assertNull($sql);
|
||||||
|
self::assertNull($params);
|
||||||
|
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_conds([], $sql, $params);
|
||||||
|
self::assertNull($sql);
|
||||||
|
self::assertNull($params);
|
||||||
|
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_conds(["col = 'value'"], $sql, $params);
|
||||||
|
self::assertSame(["col = 'value'"], $sql);
|
||||||
|
self::assertNull($params);
|
||||||
|
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_conds([["col = 'value'"]], $sql, $params);
|
||||||
|
self::assertSame(["col = 'value'"], $sql);
|
||||||
|
self::assertNull($params);
|
||||||
|
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_conds(["int" => 42, "string" => "value"], $sql, $params);
|
||||||
|
self::assertSame(["(int = :int and string = :string)"], $sql);
|
||||||
|
self::assertSame(["int" => 42, "string" => "value"], $params);
|
||||||
|
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_conds(["or", "int" => 42, "string" => "value"], $sql, $params);
|
||||||
|
self::assertSame(["(int = :int or string = :string)"], $sql);
|
||||||
|
self::assertSame(["int" => 42, "string" => "value"], $params);
|
||||||
|
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_conds([["int" => 42, "string" => "value"], ["int" => 24, "string" => "eulav"]], $sql, $params);
|
||||||
|
self::assertSame(["((int = :int and string = :string) and (int = :int1 and string = :string1))"], $sql);
|
||||||
|
self::assertSame(["int" => 42, "string" => "value", "int1" => 24, "string1" => "eulav"], $params);
|
||||||
|
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_conds(["int" => ["is null"], "string" => ["<>", "value"]], $sql, $params);
|
||||||
|
self::assertSame(["(int is null and string <> :string)"], $sql);
|
||||||
|
self::assertSame(["string" => "value"], $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testParseValues(): void {
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_values(null, $sql, $params);
|
||||||
|
self::assertNull($sql);
|
||||||
|
self::assertNull($params);
|
||||||
|
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_values([], $sql, $params);
|
||||||
|
self::assertNull($sql);
|
||||||
|
self::assertNull($params);
|
||||||
|
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_values(["col = 'value'"], $sql, $params);
|
||||||
|
self::assertSame(["col = 'value'"], $sql);
|
||||||
|
self::assertNull($params);
|
||||||
|
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_values([["col = 'value'"]], $sql, $params);
|
||||||
|
self::assertSame(["col = 'value'"], $sql);
|
||||||
|
self::assertNull($params);
|
||||||
|
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_values(["int" => 42, "string" => "value"], $sql, $params);
|
||||||
|
self::assertSame(["int = :int", "string = :string"], $sql);
|
||||||
|
self::assertSame(["int" => 42, "string" => "value"], $params);
|
||||||
|
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_values(["int" => 42, "string" => "value"], $sql, $params);
|
||||||
|
self::assertSame(["int = :int", "string = :string"], $sql);
|
||||||
|
self::assertSame(["int" => 42, "string" => "value"], $params);
|
||||||
|
|
||||||
|
$sql = $params = null;
|
||||||
|
_Query::parse_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue