modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2024-05-29 18:47:44 +04:00
parent 8254777c76
commit 48d686f20c
37 changed files with 771 additions and 461 deletions

View File

@ -0,0 +1,39 @@
<?php
namespace nur\sery\db\_private;
trait Tcreate {
static function isa(string $sql): bool {
//return preg_match("/^create(?:\s+table)?\b/i", $sql);
#XXX implémentation minimale
return preg_match("/^create\s+table\b/i", $sql);
}
static function parse(array $query, ?array &$params=null): string {
#XXX implémentation minimale
$sql = [self::merge_seq($query)];
## préfixe
if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix;
## table
$sql[] = $query["table"];
## columns
$cols = $query["cols"];
$index = 0;
foreach ($cols as $col => &$definition) {
if ($col === $index) {
$index++;
} else {
$definition = "$col $definition";
}
}; unset($definition);
$sql[] = "(".implode(", ", $cols).")";
## suffixe
if (($suffix = $query["suffix"] ?? null) !== null) $sql[] = $suffix;
## fin de la requête
return implode(" ", $sql);
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace nur\sery\db\_private;
trait Tdelete {
static function isa(string $sql): bool {
return preg_match("/^delete(?:\s+from)?\b/i", $sql);
}
static function parse(array $query, ?array &$params=null): string {
#XXX implémentation minimale
$tmpsql = self::merge_seq($query);
self::consume('delete(?:\s+from)?\b', $tmpsql);
$sql = ["delete from", $tmpsql];
## préfixe
if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix;
## table
$sql[] = $query["from"];
## where
$where = $query["where"] ?? null;
if ($where !== null) {
self::parse_conds($where, $wheresql, $params);
if ($wheresql) {
$sql[] = "where";
$sql[] = implode(" and ", $wheresql);
}
}
## suffixe
if (($suffix = $query["suffix"] ?? null) !== null) $sql[] = $suffix;
## fin de la requête
return implode(" ", $sql);
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace nur\sery\db\_private;
use nur\sery\cl;
use nur\sery\ValueException;
trait Tgeneric {
static function isa(string $sql): bool {
return preg_match('/^(?:drop\s+table)\b/i', $sql);
}
static function parse(array $query, ?array &$params=null): string {
if (!cl::is_list($query)) {
throw new ValueException("Seuls les tableaux séquentiels sont supportés");
}
return self::merge_seq($query);
}
}

View File

@ -0,0 +1,82 @@
<?php
namespace nur\sery\db\_private;
use nur\sery\cl;
use nur\sery\ValueException;
trait Tinsert {
static function isa(string $sql): bool {
return preg_match("/^insert\b/i", $sql);
}
/**
* parser une chaine de la forme
* "insert [into] [TABLE] [(COLS)] [values (VALUES)]"
*/
static function parse(array $query, ?array &$params=null): string {
# fusionner d'abord toutes les parties séquentielles
$usersql = $tmpsql = self::merge_seq($query);
### vérifier la présence des parties nécessaires
$sql = [];
if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix;
## insert
self::consume('insert\s*', $tmpsql);
$sql[] = "insert";
## into
self::consume('into\s*', $tmpsql);
$sql[] = "into";
$into = $query["into"] ?? null;
if (self::consume('([a-z_][a-z0-9_]*)\s*', $tmpsql, $ms)) {
if ($into === null) $into = $ms[1];
$sql[] = $into;
} elseif ($into !== null) {
$sql[] = $into;
} else {
throw new ValueException("expected table name: $usersql");
}
## cols & values
$usercols = [];
$uservalues = [];
if (self::consume('\(([^)]*)\)\s*', $tmpsql, $ms)) {
$usercols = array_merge($usercols, preg_split("/\s*,\s*/", $ms[1]));
}
$cols = cl::withn($query["cols"] ?? null);
$values = cl::withn($query["values"] ?? null);
$schema = $query["schema"] ?? null;
if ($cols === null) {
if ($usercols) {
$cols = $usercols;
} elseif ($values) {
$cols = array_keys($values);
$usercols = array_merge($usercols, $cols);
} elseif ($schema && is_array($schema)) {
#XXX implémenter support AssocSchema
$cols = array_keys($schema);
$usercols = array_merge($usercols, $cols);
}
}
if (self::consume('values\s+\(\s*(.*)\s*\)\s*', $tmpsql, $ms)) {
if ($ms[1]) $uservalues[] = $ms[1];
}
if ($cols !== null && !$uservalues) {
if (!$usercols) $usercols = $cols;
foreach ($cols as $col) {
$uservalues[] = ":$col";
$params[$col] = $values[$col] ?? null;
}
}
$sql[] = "(" . implode(", ", $usercols) . ")";
$sql[] = "values (" . implode(", ", $uservalues) . ")";
## suffixe
if (($suffix = $query["suffix"] ?? null) !== null) $sql[] = $suffix;
## fin de la requête
self::check_eof($tmpsql, $usersql);
return implode(" ", $sql);
}
}

157
src/db/_private/Tselect.php Normal file
View File

@ -0,0 +1,157 @@
<?php
namespace nur\sery\db\_private;
use nur\sery\cl;
use nur\sery\ValueException;
trait Tselect {
static function isa(string $sql): bool {
return preg_match("/^select\b/i", $sql);
}
/**
* parser une chaine de la forme
* "select [COLS] [from TABLE] [where CONDS] [order by ORDERS] [group by GROUPS] [having CONDS]"
*/
static function parse(array $query, ?array &$params=null): string {
# fusionner d'abord toutes les parties séquentielles
$usersql = $tmpsql = self::merge_seq($query);
### vérifier la présence des parties nécessaires
$sql = [];
## préfixe
if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix;
## select
self::consume('(select(?:\s*distinct)?)\s*', $tmpsql, $ms);
$sql[] = $ms[1];
## cols
$usercols = [];
if (self::consume('(.*?)\s*(?=$|\bfrom\b)', $tmpsql, $ms)) {
if ($ms[1]) $usercols[] = $ms[1];
}
$tmpcols = cl::withn($query["cols"] ?? null);
$schema = $query["schema"] ?? null;
if ($tmpcols !== null) {
$cols = [];
$index = 0;
foreach ($tmpcols as $key => $col) {
if ($key === $index) {
$index++;
$cols[] = $col;
$usercols[] = $col;
} else {
$cols[] = $key;
$usercols[] = "$col as $key";
}
}
} else {
$cols = null;
if ($schema && is_array($schema) && !in_array("*", $usercols)) {
$cols = array_keys($schema);
$usercols = array_merge($usercols, $cols);
}
}
if (!$usercols && !$cols) $usercols = ["*"];
$sql[] = implode(", ", $usercols);
## from
$from = $query["from"] ?? null;
if (self::consume('from\s+([a-z_][a-z0-9_]*)\s*(?=;?\s*$|\bwhere\b)', $tmpsql, $ms)) {
if ($from === null) $from = $ms[1];
$sql[] = "from";
$sql[] = $from;
} elseif ($from !== null) {
$sql[] = "from";
$sql[] = $from;
} else {
throw new ValueException("expected table name: $usersql");
}
## where
$userwhere = [];
if (self::consume('where\b\s*(.*?)(?=;?\s*$|\border\s+by\b)', $tmpsql, $ms)) {
if ($ms[1]) $userwhere[] = $ms[1];
}
$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
$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++;
} else {
if ($value === null) $value = false;
if (!is_bool($value)) {
$userorderby[] = "$key $value";
} elseif ($value) {
$userorderby[] = $key;
}
}
}
}
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++;
} else {
if ($value === null) $value = false;
if (!is_bool($value)) {
$usergroupby[] = "$key $value";
} elseif ($value) {
$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);
}
## suffixe
if (($suffix = $query["suffix"] ?? null) !== null) $sql[] = $suffix;
## fin de la requête
self::check_eof($tmpsql, $usersql);
return implode(" ", $sql);
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace nur\sery\db\_private;
trait Tupdate {
static function isa(string $sql): bool {
return preg_match("/^update\b/i", $sql);
}
static function parse(array $query, ?array &$params=null): string {
#XXX implémentation minimale
$sql = [self::merge_seq($query)];
## préfixe
if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix;
## table
$sql[] = $query["table"];
## set
self::parse_set_values($query["values"], $setsql, $params);
$sql[] = "set";
$sql[] = implode(", ", $setsql);
## where
$where = $query["where"] ?? null;
if ($where !== null) {
self::parse_conds($where, $wheresql, $params);
if ($wheresql) {
$sql[] = "where";
$sql[] = implode(" and ", $wheresql);
}
}
## suffixe
if (($suffix = $query["suffix"] ?? null) !== null) $sql[] = $suffix;
## fin de la requête
return implode(" ", $sql);
}
}

View File

@ -1,38 +1,11 @@
<?php <?php
namespace nur\sery\db\sqlite; namespace nur\sery\db\_private;
use nur\sery\cl; use nur\sery\cl;
use nur\sery\str; use nur\sery\str;
use nur\sery\ValueException; use nur\sery\ValueException;
use SQLite3;
use SQLite3Stmt;
class _query {
static function verifix(&$query, ?array &$params=null): void {
if (is_array($query)) {
$prefix = $query[0] ?? null;
if ($prefix === null) {
throw new ValueException("requête invalide");
} elseif (_query_create::isa($prefix)) {
$query = _query_create::parse($query, $params);
} elseif (_query_select::isa($prefix)) {
$query = _query_select::parse($query, $params);
} elseif (_query_insert::isa($prefix)) {
$query = _query_insert::parse($query, $params);
} elseif (_query_update::isa($prefix)) {
$query = _query_update::parse($query, $params);
} elseif (_query_delete::isa($prefix)) {
$query = _query_delete::parse($query, $params);
} elseif (_query_generic::isa($prefix)) {
$query = _query_generic::parse($query, $params);
} else {
throw SqliteException::wrap(ValueException::invalid_kind($query, "query"));
}
} elseif (!is_string($query)) {
$query = strval($query);
}
}
abstract class _base {
protected static function consume(string $pattern, string &$string, ?array &$ms=null): bool { protected static function consume(string $pattern, string &$string, ?array &$ms=null): bool {
if (!preg_match("/^$pattern/i", $string, $ms)) return false; if (!preg_match("/^$pattern/i", $string, $ms)) return false;
$string = substr($string, strlen($ms[0])); $string = substr($string, strlen($ms[0]));
@ -72,6 +45,8 @@ class _query {
## séquentiel ## séquentiel
if ($index === 0 && self::is_sep($cond)) { if ($index === 0 && self::is_sep($cond)) {
$sep = $cond; $sep = $cond;
} elseif (is_bool($cond)) {
# ignorer les valeurs true et false
} elseif (is_array($cond)) { } elseif (is_array($cond)) {
# condition récursive # condition récursive
self::parse_conds($cond, $condsql, $params); self::parse_conds($cond, $condsql, $params);
@ -80,6 +55,12 @@ class _query {
$condsql[] = strval($cond); $condsql[] = strval($cond);
} }
$index++; $index++;
} elseif ($cond === false) {
## associatif
# condition litérale ignorée car condition false
} elseif ($cond === true) {
# condition litérale sélectionnée car condition true
$condsql[] = strval($key);
} else { } else {
## associatif ## associatif
# paramètre # paramètre
@ -230,8 +211,10 @@ class _query {
} }
} }
abstract protected static function verifix(&$sql, ?array &$params=null): void;
function __construct($sql, ?array $params=null) { function __construct($sql, ?array $params=null) {
self::verifix($sql, $params); static::verifix($sql, $params);
$this->sql = $sql; $this->sql = $sql;
$this->params = $params; $this->params = $params;
} }
@ -241,24 +224,4 @@ class _query {
/** @var ?array */ /** @var ?array */
protected $params; protected $params;
function useStmt(SQLite3 $db, ?SQLite3Stmt &$stmt=null, ?string &$sql=null): bool {
if ($this->params !== null) {
/** @var SQLite3Stmt $stmt */
$stmt = SqliteException::check($db, $db->prepare($this->sql));
$close = true;
try {
foreach ($this->params as $param => $value) {
SqliteException::check($db, $stmt->bindValue($param, $value));
}
$close = false;
return true;
} finally {
if ($close) $stmt->close();
}
} else {
$sql = $this->sql;
return false;
}
}
} }

View File

@ -0,0 +1,12 @@
<?php
namespace nur\sery\db\_private;
class _create {
const SCHEMA = [
"prefix" => "?string",
"table" => "string",
"schema" => "?array",
"cols" => "?array",
"suffix" => "?string",
];
}

View File

@ -0,0 +1,11 @@
<?php
namespace nur\sery\db\_private;
class _delete {
const SCHEMA = [
"prefix" => "?string",
"from" => "?string",
"where" => "?array",
"suffix" => "?string",
];
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\db\_private;
use nur\sery\cl;
use nur\sery\ValueException;
class _generic {
const SCHEMA = [
];
}

View File

@ -0,0 +1,16 @@
<?php
namespace nur\sery\db\_private;
use nur\sery\cl;
use nur\sery\ValueException;
class _insert {
const SCHEMA = [
"prefix" => "?string",
"into" => "?string",
"schema" => "?array",
"cols" => "?array",
"values" => "?array",
"suffix" => "?string",
];
}

View File

@ -0,0 +1,19 @@
<?php
namespace nur\sery\db\_private;
use nur\sery\cl;
use nur\sery\ValueException;
class _select {
const SCHEMA = [
"prefix" => "?string",
"schema" => "?array",
"cols" => "?array",
"from" => "?string",
"where" => "?array",
"order by" => "?array",
"group by" => "?array",
"having" => "?array",
"suffix" => "?string",
];
}

View File

@ -0,0 +1,14 @@
<?php
namespace nur\sery\db\_private;
class _update {
const SCHEMA = [
"prefix" => "?string",
"table" => "?string",
"schema" => "?array",
"cols" => "?array",
"values" => "?array",
"where" => "?array",
"suffix" => "?string",
];
}

View File

@ -0,0 +1,32 @@
<?php
namespace nur\sery\db\mysql;
use nur\sery\db\_private\_base;
use nur\sery\ValueException;
class _query_base extends _base {
protected static function verifix(&$sql, ?array &$params=null): void {
if (is_array($sql)) {
$prefix = $sql[0] ?? null;
if ($prefix === null) {
throw new ValueException("requête invalide");
} elseif (_query_create::isa($prefix)) {
$sql = _query_create::parse($sql, $params);
} elseif (_query_select::isa($prefix)) {
$sql = _query_select::parse($sql, $params);
} elseif (_query_insert::isa($prefix)) {
$sql = _query_insert::parse($sql, $params);
} elseif (_query_update::isa($prefix)) {
$sql = _query_update::parse($sql, $params);
} elseif (_query_delete::isa($prefix)) {
$sql = _query_delete::parse($sql, $params);
} elseif (_query_generic::isa($prefix)) {
$sql = _query_generic::parse($sql, $params);
} else {
throw ValueException::invalid_kind($sql, "query");
}
} elseif (!is_string($sql)) {
$sql = strval($sql);
}
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\db\mysql;
use nur\sery\db\_private\_create;
use nur\sery\db\_private\Tcreate;
class _query_create extends _query_base {
use Tcreate;
const SCHEMA = _create::SCHEMA;
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\db\mysql;
use nur\sery\db\_private\_delete;
use nur\sery\db\_private\Tdelete;
class _query_delete extends _query_base {
use Tdelete;
const SCHEMA = _delete::SCHEMA;
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\db\mysql;
use nur\sery\db\_private\_generic;
use nur\sery\db\_private\Tgeneric;
class _query_generic extends _query_base {
use Tgeneric;
const SCHEMA = _generic::SCHEMA;
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\db\mysql;
use nur\sery\db\_private\_insert;
use nur\sery\db\_private\Tinsert;
class _query_insert extends _query_base {
use Tinsert;
const SCHEMA = _insert::SCHEMA;
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\db\mysql;
use nur\sery\db\_private\_select;
use nur\sery\db\_private\Tselect;
class _query_select extends _query_base {
use Tselect;
const SCHEMA = _select::SCHEMA;
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\db\mysql;
use nur\sery\db\_private\_update;
use nur\sery\db\_private\Tupdate;
class _query_update extends _query_base {
use Tupdate;
const SCHEMA = _update::SCHEMA;
}

12
src/db/mysql/query.php Normal file
View File

@ -0,0 +1,12 @@
<?php
namespace nur\sery\db\mysql;
/**
* Class query: classe outil temporaire pour générer les requêtes
*/
class query extends _query_base {
static function with($sql, ?array $params=null): array {
self::verifix($sql, $params);
return [$sql, $params];
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace nur\sery\db\pdo;
use nur\sery\db\_private\_base;
use nur\sery\ValueException;
class _query_base extends _base {
protected static function verifix(&$sql, ?array &$params=null): void {
if (is_array($sql)) {
$prefix = $sql[0] ?? null;
if ($prefix === null) {
throw new ValueException("requête invalide");
} elseif (_query_create::isa($prefix)) {
$sql = _query_create::parse($sql, $params);
} elseif (_query_select::isa($prefix)) {
$sql = _query_select::parse($sql, $params);
} elseif (_query_insert::isa($prefix)) {
$sql = _query_insert::parse($sql, $params);
} elseif (_query_update::isa($prefix)) {
$sql = _query_update::parse($sql, $params);
} elseif (_query_delete::isa($prefix)) {
$sql = _query_delete::parse($sql, $params);
} elseif (_query_generic::isa($prefix)) {
$sql = _query_generic::parse($sql, $params);
} else {
throw ValueException::invalid_kind($sql, "query");
}
} elseif (!is_string($sql)) {
$sql = strval($sql);
}
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\db\pdo;
use nur\sery\db\_private\_create;
use nur\sery\db\_private\Tcreate;
class _query_create extends _query_base {
use Tcreate;
const SCHEMA = _create::SCHEMA;
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\db\pdo;
use nur\sery\db\_private\_delete;
use nur\sery\db\_private\Tdelete;
class _query_delete extends _query_base {
use Tdelete;
const SCHEMA = _delete::SCHEMA;
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\db\pdo;
use nur\sery\db\_private\_generic;
use nur\sery\db\_private\Tgeneric;
class _query_generic extends _query_base {
use Tgeneric;
const SCHEMA = _generic::SCHEMA;
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\db\pdo;
use nur\sery\db\_private\_insert;
use nur\sery\db\_private\Tinsert;
class _query_insert extends _query_base {
use Tinsert;
const SCHEMA = _insert::SCHEMA;
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\db\pdo;
use nur\sery\db\_private\_select;
use nur\sery\db\_private\Tselect;
class _query_select extends _query_base {
use Tselect;
const SCHEMA = _select::SCHEMA;
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\sery\db\pdo;
use nur\sery\db\_private\_update;
use nur\sery\db\_private\Tupdate;
class _query_update extends _query_base {
use Tupdate;
const SCHEMA = _update::SCHEMA;
}

View File

@ -147,7 +147,7 @@ class Sqlite {
function exec($query, ?array $params=null): bool { function exec($query, ?array $params=null): bool {
$db = $this->db(); $db = $this->db();
$query = new _query($query, $params); $query = new _query_base($query, $params);
if ($query->useStmt($db, $stmt, $sql)) { if ($query->useStmt($db, $stmt, $sql)) {
try { try {
return $stmt->execute()->finalize(); return $stmt->execute()->finalize();
@ -177,7 +177,7 @@ class Sqlite {
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_base($query, $params);
if ($query->useStmt($db, $stmt, $sql)) { if ($query->useStmt($db, $stmt, $sql)) {
try { try {
$result = $this->checkResult($stmt->execute()); $result = $this->checkResult($stmt->execute());
@ -224,7 +224,7 @@ class Sqlite {
*/ */
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_base($query, $params);
if ($query->useStmt($db, $stmt, $sql)) { if ($query->useStmt($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);

View File

@ -0,0 +1,54 @@
<?php
namespace nur\sery\db\sqlite;
use nur\sery\db\_private\_base;
use nur\sery\ValueException;
use SQLite3;
use SQLite3Stmt;
class _query_base extends _base {
protected static function verifix(&$sql, ?array &$params=null): void {
if (is_array($sql)) {
$prefix = $sql[0] ?? null;
if ($prefix === null) {
throw new ValueException("requête invalide");
} elseif (_query_create::isa($prefix)) {
$sql = _query_create::parse($sql, $params);
} elseif (_query_select::isa($prefix)) {
$sql = _query_select::parse($sql, $params);
} elseif (_query_insert::isa($prefix)) {
$sql = _query_insert::parse($sql, $params);
} elseif (_query_update::isa($prefix)) {
$sql = _query_update::parse($sql, $params);
} elseif (_query_delete::isa($prefix)) {
$sql = _query_delete::parse($sql, $params);
} elseif (_query_generic::isa($prefix)) {
$sql = _query_generic::parse($sql, $params);
} else {
throw SqliteException::wrap(ValueException::invalid_kind($sql, "query"));
}
} elseif (!is_string($sql)) {
$sql = strval($sql);
}
}
function useStmt(SQLite3 $db, ?SQLite3Stmt &$stmt=null, ?string &$sql=null): bool {
if ($this->params !== null) {
/** @var SQLite3Stmt $stmt */
$stmt = SqliteException::check($db, $db->prepare($this->sql));
$close = true;
try {
foreach ($this->params as $param => $value) {
SqliteException::check($db, $stmt->bindValue($param, $value));
}
$close = false;
return true;
} finally {
if ($close) $stmt->close();
}
} else {
$sql = $this->sql;
return false;
}
}
}

View File

@ -1,47 +1,10 @@
<?php <?php
namespace nur\sery\db\sqlite; namespace nur\sery\db\sqlite;
class _query_create extends _query { use nur\sery\db\_private\_create;
const SCHEMA = [ use nur\sery\db\_private\Tcreate;
"prefix" => "?string",
"table" => "string",
"schema" => "?array",
"cols" => "?array",
"suffix" => "?string",
];
static function isa(string $sql): bool { class _query_create extends _query_base {
//return preg_match("/^create(?:\s+table)?\b/i", $sql); use Tcreate;
#XXX implémentation minimale const SCHEMA = _create::SCHEMA;
return preg_match("/^create\s+table\b/i", $sql);
}
static function parse(array $query, ?array &$params=null): string {
#XXX implémentation minimale
$sql = [self::merge_seq($query)];
## préfixe
if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix;
## table
$sql[] = $query["table"];
## columns
$cols = $query["cols"];
$index = 0;
foreach ($cols as $col => &$definition) {
if ($col === $index) {
$index++;
} else {
$definition = "$col $definition";
}
}; unset($definition);
$sql[] = "(".implode(", ", $cols).")";
## suffixe
if (($suffix = $query["suffix"] ?? null) !== null) $sql[] = $suffix;
## fin de la requête
return implode(" ", $sql);
}
} }

View File

@ -1,44 +1,10 @@
<?php <?php
namespace nur\sery\db\sqlite; namespace nur\sery\db\sqlite;
class _query_delete extends _query { use nur\sery\db\_private\_delete;
const SCHEMA = [ use nur\sery\db\_private\Tdelete;
"prefix" => "?string",
"from" => "?string",
"where" => "?array",
"suffix" => "?string",
];
static function isa(string $sql): bool { class _query_delete extends _query_base {
//return preg_match("/^delete(?:\s+from)?\b/i", $sql); use Tdelete;
#XXX implémentation minimale const SCHEMA = _delete::SCHEMA;
return preg_match("/^delete\s+from\b/i", $sql);
}
static function parse(array $query, ?array &$params=null): string {
#XXX implémentation minimale
$sql = [self::merge_seq($query)];
## préfixe
if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix;
## table
$sql[] = $query["table"];
## where
$where = $query["where"] ?? null;
if ($where !== null) {
_query::parse_conds($where, $wheresql, $params);
if ($wheresql) {
$sql[] = "where";
$sql[] = implode(" and ", $wheresql);
}
}
## suffixe
if (($suffix = $query["suffix"] ?? null) !== null) $sql[] = $suffix;
## fin de la requête
return implode(" ", $sql);
}
} }

View File

@ -1,18 +1,10 @@
<?php <?php
namespace nur\sery\db\sqlite; namespace nur\sery\db\sqlite;
use nur\sery\cl; use nur\sery\db\_private\_generic;
use nur\sery\ValueException; use nur\sery\db\_private\Tgeneric;
class _query_generic extends _query { class _query_generic extends _query_base {
static function isa(string $sql): bool { use Tgeneric;
return preg_match('/^(?:drop\s+table)\b/i', $sql); const SCHEMA = _generic::SCHEMA;
}
static function parse(array $query, ?array &$params=null): string {
if (!cl::is_list($query)) {
throw new ValueException("Seuls les tableaux séquentiels sont supportés");
}
return self::merge_seq($query);
}
} }

View File

@ -1,91 +1,10 @@
<?php <?php
namespace nur\sery\db\sqlite; namespace nur\sery\db\sqlite;
use nur\sery\cl; use nur\sery\db\_private\_insert;
use nur\sery\ValueException; use nur\sery\db\_private\Tinsert;
class _query_insert extends _query { class _query_insert extends _query_base {
const SCHEMA = [ use Tinsert;
"prefix" => "?string", const SCHEMA = _insert::SCHEMA;
"into" => "?string",
"schema" => "?array",
"cols" => "?array",
"values" => "?array",
"suffix" => "?string",
];
static function isa(string $sql): bool {
return preg_match("/^insert\b/i", $sql);
}
/**
* parser une chaine de la forme
* "insert [into] [TABLE] [(COLS)] [values (VALUES)]"
*/
static function parse(array $query, ?array &$params=null): string {
# fusionner d'abord toutes les parties séquentielles
$usersql = $tmpsql = self::merge_seq($query);
### vérifier la présence des parties nécessaires
$sql = [];
if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix;
## insert
self::consume('insert\s*', $tmpsql);
$sql[] = "insert";
## into
self::consume('into\s*', $tmpsql);
$sql[] = "into";
$into = $query["into"] ?? null;
if (self::consume('([a-z_][a-z0-9_]*)\s*', $tmpsql, $ms)) {
if ($into === null) $into = $ms[1];
$sql[] = $into;
} elseif ($into !== null) {
$sql[] = $into;
} else {
throw new ValueException("expected table name: $usersql");
}
## cols & values
$usercols = [];
$uservalues = [];
if (self::consume('\(([^)]*)\)\s*', $tmpsql, $ms)) {
$usercols = array_merge($usercols, preg_split("/\s*,\s*/", $ms[1]));
}
$cols = cl::withn($query["cols"] ?? null);
$values = cl::withn($query["values"] ?? null);
$schema = $query["schema"] ?? null;
if ($cols === null) {
if ($usercols) {
$cols = $usercols;
} elseif ($values) {
$cols = array_keys($values);
$usercols = array_merge($usercols, $cols);
} elseif ($schema && is_array($schema)) {
#XXX implémenter support AssocSchema
$cols = array_keys($schema);
$usercols = array_merge($usercols, $cols);
}
}
if (self::consume('values\s+\(\s*(.*)\s*\)\s*', $tmpsql, $ms)) {
if ($ms[1]) $uservalues[] = $ms[1];
}
if ($cols !== null && !$uservalues) {
if (!$usercols) $usercols = $cols;
foreach ($cols as $col) {
$uservalues[] = ":$col";
$params[$col] = $values[$col] ?? null;
}
}
$sql[] = "(" . implode(", ", $usercols) . ")";
$sql[] = "values (" . implode(", ", $uservalues) . ")";
## suffixe
if (($suffix = $query["suffix"] ?? null) !== null) $sql[] = $suffix;
## fin de la requête
self::check_eof($tmpsql, $usersql);
return implode(" ", $sql);
}
} }

View File

@ -1,169 +1,10 @@
<?php <?php
namespace nur\sery\db\sqlite; namespace nur\sery\db\sqlite;
use nur\sery\cl; use nur\sery\db\_private\_select;
use nur\sery\ValueException; use nur\sery\db\_private\Tselect;
class _query_select extends _query { class _query_select extends _query_base {
const SCHEMA = [ use Tselect;
"prefix" => "?string", const SCHEMA = _select::SCHEMA;
"schema" => "?array",
"cols" => "?array",
"from" => "?string",
"where" => "?array",
"order by" => "?array",
"group by" => "?array",
"having" => "?array",
"suffix" => "?string",
];
static function isa(string $sql): bool {
return preg_match("/^select\b/i", $sql);
}
/**
* parser une chaine de la forme
* "select [COLS] [from TABLE] [where CONDS] [order by ORDERS] [group by GROUPS] [having CONDS]"
*/
static function parse(array $query, ?array &$params=null): string {
# fusionner d'abord toutes les parties séquentielles
$usersql = $tmpsql = self::merge_seq($query);
### vérifier la présence des parties nécessaires
$sql = [];
## préfixe
if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix;
## select
self::consume('(select(?:\s*distinct)?)\s*', $tmpsql, $ms);
$sql[] = $ms[1];
## cols
$usercols = [];
if (self::consume('(.*?)\s*(?=$|\bfrom\b)', $tmpsql, $ms)) {
if ($ms[1]) $usercols[] = $ms[1];
}
$tmpcols = cl::withn($query["cols"] ?? null);
$schema = $query["schema"] ?? null;
if ($tmpcols !== null) {
$cols = [];
$index = 0;
foreach ($tmpcols as $key => $col) {
if ($key === $index) {
$index++;
$cols[] = $col;
$usercols[] = $col;
} else {
$cols[] = $key;
$usercols[] = "$col as $key";
}
}
} else {
$cols = null;
if ($schema && is_array($schema) && !in_array("*", $usercols)) {
$cols = array_keys($schema);
$usercols = array_merge($usercols, $cols);
}
}
if (!$usercols && !$cols) $usercols = ["*"];
$sql[] = implode(", ", $usercols);
## from
$from = $query["from"] ?? null;
if (self::consume('from\s+([a-z_][a-z0-9_]*)\s*(?=;?\s*$|\bwhere\b)', $tmpsql, $ms)) {
if ($from === null) $from = $ms[1];
$sql[] = "from";
$sql[] = $from;
} elseif ($from !== null) {
$sql[] = "from";
$sql[] = $from;
} else {
throw new ValueException("expected table name: $usersql");
}
## where
$userwhere = [];
if (self::consume('where\b\s*(.*?)(?=;?\s*$|\border\s+by\b)', $tmpsql, $ms)) {
if ($ms[1]) $userwhere[] = $ms[1];
}
$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
$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++;
} else {
if ($value === null) $value = false;
if (!is_bool($value)) {
$userorderby[] = "$key $value";
} elseif ($value) {
$userorderby[] = $key;
}
}
}
}
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++;
} else {
if ($value === null) $value = false;
if (!is_bool($value)) {
$usergroupby[] = "$key $value";
} elseif ($value) {
$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);
}
## suffixe
if (($suffix = $query["suffix"] ?? null) !== null) $sql[] = $suffix;
## fin de la requête
self::check_eof($tmpsql, $usersql);
return implode(" ", $sql);
}
} }

View File

@ -1,50 +1,10 @@
<?php <?php
namespace nur\sery\db\sqlite; namespace nur\sery\db\sqlite;
class _query_update extends _query { use nur\sery\db\_private\_update;
const SCHEMA = [ use nur\sery\db\_private\Tupdate;
"prefix" => "?string",
"table" => "?string",
"schema" => "?array",
"cols" => "?array",
"values" => "?array",
"where" => "?array",
"suffix" => "?string",
];
static function isa(string $sql): bool { class _query_update extends _query_base {
return preg_match("/^update\b/i", $sql); use Tupdate;
} const SCHEMA = _update::SCHEMA;
static function parse(array $query, ?array &$params=null): string {
#XXX implémentation minimale
$sql = [self::merge_seq($query)];
## préfixe
if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix;
## table
$sql[] = $query["table"];
## set
_query::parse_set_values($query["values"], $setsql, $params);
$sql[] = "set";
$sql[] = implode(", ", $setsql);
## where
$where = $query["where"] ?? null;
if ($where !== null) {
_query::parse_conds($where, $wheresql, $params);
if ($wheresql) {
$sql[] = "where";
$sql[] = implode(" and ", $wheresql);
}
}
## suffixe
if (($suffix = $query["suffix"] ?? null) !== null) $sql[] = $suffix;
## fin de la requête
return implode(" ", $sql);
}
} }

View File

@ -6,99 +6,99 @@ use PHPUnit\Framework\TestCase;
class _queryTest extends TestCase { class _queryTest extends TestCase {
function testParseConds(): void { function testParseConds(): void {
$sql = $params = null; $sql = $params = null;
_query::parse_conds(null, $sql, $params); _query_base::parse_conds(null, $sql, $params);
self::assertNull($sql); self::assertNull($sql);
self::assertNull($params); self::assertNull($params);
$sql = $params = null; $sql = $params = null;
_query::parse_conds([], $sql, $params); _query_base::parse_conds([], $sql, $params);
self::assertNull($sql); self::assertNull($sql);
self::assertNull($params); self::assertNull($params);
$sql = $params = null; $sql = $params = null;
_query::parse_conds(["col" => null], $sql, $params); _query_base::parse_conds(["col" => null], $sql, $params);
self::assertSame(["col is null"], $sql); self::assertSame(["col is null"], $sql);
self::assertNull($params); self::assertNull($params);
$sql = $params = null; $sql = $params = null;
_query::parse_conds(["col = 'value'"], $sql, $params); _query_base::parse_conds(["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_conds([["col = 'value'"]], $sql, $params); _query_base::parse_conds([["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_conds(["int" => 42, "string" => "value"], $sql, $params); _query_base::parse_conds(["int" => 42, "string" => "value"], $sql, $params);
self::assertSame(["(int = :int and string = :string)"], $sql); self::assertSame(["(int = :int and 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_conds(["or", "int" => 42, "string" => "value"], $sql, $params); _query_base::parse_conds(["or", "int" => 42, "string" => "value"], $sql, $params);
self::assertSame(["(int = :int or string = :string)"], $sql); self::assertSame(["(int = :int or 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_conds([["int" => 42, "string" => "value"], ["int" => 24, "string" => "eulav"]], $sql, $params); _query_base::parse_conds([["int" => 42, "string" => "value"], ["int" => 24, "string" => "eulav"]], $sql, $params);
self::assertSame(["((int = :int and string = :string) and (int = :int2 and string = :string2))"], $sql); self::assertSame(["((int = :int and string = :string) and (int = :int2 and string = :string2))"], $sql);
self::assertSame(["int" => 42, "string" => "value", "int2" => 24, "string2" => "eulav"], $params); self::assertSame(["int" => 42, "string" => "value", "int2" => 24, "string2" => "eulav"], $params);
$sql = $params = null; $sql = $params = null;
_query::parse_conds(["int" => ["is null"], "string" => ["<>", "value"]], $sql, $params); _query_base::parse_conds(["int" => ["is null"], "string" => ["<>", "value"]], $sql, $params);
self::assertSame(["(int is null and string <> :string)"], $sql); self::assertSame(["(int is null and string <> :string)"], $sql);
self::assertSame(["string" => "value"], $params); self::assertSame(["string" => "value"], $params);
$sql = $params = null; $sql = $params = null;
_query::parse_conds(["col" => ["between", "lower", "upper"]], $sql, $params); _query_base::parse_conds(["col" => ["between", "lower", "upper"]], $sql, $params);
self::assertSame(["col between :col and :col2"], $sql); self::assertSame(["col between :col and :col2"], $sql);
self::assertSame(["col" => "lower", "col2" => "upper"], $params); self::assertSame(["col" => "lower", "col2" => "upper"], $params);
$sql = $params = null; $sql = $params = null;
_query::parse_conds(["col" => ["in", "one"]], $sql, $params); _query_base::parse_conds(["col" => ["in", "one"]], $sql, $params);
self::assertSame(["col in (:col)"], $sql); self::assertSame(["col in (:col)"], $sql);
self::assertSame(["col" => "one"], $params); self::assertSame(["col" => "one"], $params);
$sql = $params = null; $sql = $params = null;
_query::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);
} }
function testParseValues(): void { function testParseValues(): void {
$sql = $params = null; $sql = $params = null;
_query::parse_set_values(null, $sql, $params); _query_base::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_set_values([], $sql, $params); _query_base::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_set_values(["col = 'value'"], $sql, $params); _query_base::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_set_values([["col = 'value'"]], $sql, $params); _query_base::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_set_values(["int" => 42, "string" => "value"], $sql, $params); _query_base::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_set_values(["int" => 42, "string" => "value"], $sql, $params); _query_base::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_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 = :int2", "string = :string2"], $sql); self::assertSame(["int = :int", "string = :string", "int = :int2", "string = :string2"], $sql);
self::assertSame(["int" => 42, "string" => "value", "int2" => 24, "string2" => "eulav"], $params); self::assertSame(["int" => 42, "string" => "value", "int2" => 24, "string2" => "eulav"], $params);
} }