diff --git a/php/src/db/IDatabase.php b/php/src/db/IDatabase.php index 497e5be..dc71720 100644 --- a/php/src/db/IDatabase.php +++ b/php/src/db/IDatabase.php @@ -15,5 +15,9 @@ interface IDatabase extends ITransactor { function one($query, ?array $params=null): ?array; + /** + * si $primaryKeys est fourni, le résultat est indexé sur la(es) colonne(s) + * spécifiée(s) + */ function all($query, ?array $params=null, $primaryKeys=null): iterable; } diff --git a/php/src/db/_private/_base.php b/php/src/db/_private/_base.php index 14568cb..5e366e6 100644 --- a/php/src/db/_private/_base.php +++ b/php/src/db/_private/_base.php @@ -6,7 +6,51 @@ use nulib\str; use nulib\ValueException; abstract class _base extends _common { - abstract protected static function verifix(&$sql, ?array &$bindings=null, ?array &$meta=null): void; + protected static function verifix(&$sql, ?array &$bindings=null, ?array &$meta=null): void { + if (is_array($sql)) { + $prefix = $sql[0] ?? null; + if ($prefix === null) { + throw new ValueException("requête invalide"); + } elseif (_create::isa($prefix)) { + $sql = _create::parse($sql, $bindings); + $meta = ["isa" => "create", "type" => "ddl"]; + } elseif (_select::isa($prefix)) { + $sql = _select::parse($sql, $bindings); + $meta = ["isa" => "select", "type" => "dql"]; + } elseif (_insert::isa($prefix)) { + $sql = _insert::parse($sql, $bindings); + $meta = ["isa" => "insert", "type" => "dml"]; + } elseif (_update::isa($prefix)) { + $sql = _update::parse($sql, $bindings); + $meta = ["isa" => "update", "type" => "dml"]; + } elseif (_delete::isa($prefix)) { + $sql = _delete::parse($sql, $bindings); + $meta = ["isa" => "delete", "type" => "dml"]; + } elseif (_generic::isa($prefix)) { + $sql = _generic::parse($sql, $bindings); + $meta = ["isa" => "generic", "type" => null]; + } else { + throw ValueException::invalid_kind($sql, "query"); + } + } else { + if (!is_string($sql)) $sql = strval($sql); + if (_create::isa($sql)) { + $meta = ["isa" => "create", "type" => "ddl"]; + } elseif (_select::isa($sql)) { + $meta = ["isa" => "select", "type" => "dql"]; + } elseif (_insert::isa($sql)) { + $meta = ["isa" => "insert", "type" => "dml"]; + } elseif (_update::isa($sql)) { + $meta = ["isa" => "update", "type" => "dml"]; + } elseif (_delete::isa($sql)) { + $meta = ["isa" => "delete", "type" => "dml"]; + } elseif (_generic::isa($sql)) { + $meta = ["isa" => "generic", "type" => null]; + } else { + $meta = ["isa" => "generic", "type" => null]; + } + } + } static function with($sql, ?array $params=null): array { static::verifix($sql, $params); diff --git a/php/src/db/pdo/Pdo.php b/php/src/db/pdo/Pdo.php index fb8b9b8..286dbeb 100644 --- a/php/src/db/pdo/Pdo.php +++ b/php/src/db/pdo/Pdo.php @@ -143,11 +143,6 @@ class Pdo implements IDatabase { return $this->db()->exec($query); } - private static function is_insert(?string $sql): bool { - if ($sql === null) return false; - return preg_match('/^\s*insert\b/i', $sql); - } - function exec($query, ?array $params=null) { $db = $this->db(); $query = new query($query, $params); @@ -157,7 +152,7 @@ class Pdo implements IDatabase { else return $stmt->rowCount(); } else { $rowCount = $db->exec($sql); - if (self::is_insert($sql)) return $db->lastInsertId(); + if ($query->isInsert()) return $db->lastInsertId(); else return $rowCount; } } @@ -245,11 +240,7 @@ class Pdo implements IDatabase { return $this->get($query, $params, true); } - /** - * si $primaryKeys est fourni, le résultat est indexé sur la(es) colonne(s) - * spécifiée(s) - */ - function all($query, ?array $params=null, $primaryKeys=null): Generator { + function all($query, ?array $params=null, $primaryKeys=null): iterable { $db = $this->db(); $query = new query($query, $params); $stmt = null; @@ -260,7 +251,7 @@ class Pdo implements IDatabase { } else { $stmt = $db->query($sql); } - if ($primaryKeys !== null) $primaryKeys = cl::with($primaryKeys); + $primaryKeys = cl::withn($primaryKeys); while (($row = $stmt->fetch(\PDO::FETCH_ASSOC)) !== false) { $this->verifixRow($row); if ($primaryKeys !== null) { diff --git a/php/src/db/pdo/query.php b/php/src/db/pdo/query.php index f57cf4b..5d6962f 100644 --- a/php/src/db/pdo/query.php +++ b/php/src/db/pdo/query.php @@ -2,64 +2,11 @@ namespace nulib\db\pdo; use nulib\db\_private\_base; -use nulib\db\_private\_create; -use nulib\db\_private\_delete; -use nulib\db\_private\_generic; -use nulib\db\_private\_insert; -use nulib\db\_private\_select; -use nulib\db\_private\_update; use nulib\db\_private\Tbindings; -use nulib\ValueException; class query extends _base { use Tbindings; - protected static function verifix(&$sql, ?array &$bindings=null, ?array &$meta=null): void { - if (is_array($sql)) { - $prefix = $sql[0] ?? null; - if ($prefix === null) { - throw new ValueException("requête invalide"); - } elseif (_create::isa($prefix)) { - $sql = _create::parse($sql, $bindings); - $meta = ["isa" => "create", "type" => "ddl"]; - } elseif (_select::isa($prefix)) { - $sql = _select::parse($sql, $bindings); - $meta = ["isa" => "select", "type" => "dql"]; - } elseif (_insert::isa($prefix)) { - $sql = _insert::parse($sql, $bindings); - $meta = ["isa" => "insert", "type" => "dml"]; - } elseif (_update::isa($prefix)) { - $sql = _update::parse($sql, $bindings); - $meta = ["isa" => "update", "type" => "dml"]; - } elseif (_delete::isa($prefix)) { - $sql = _delete::parse($sql, $bindings); - $meta = ["isa" => "delete", "type" => "dml"]; - } elseif (_generic::isa($prefix)) { - $sql = _generic::parse($sql, $bindings); - $meta = ["isa" => "generic", "type" => null]; - } else { - throw ValueException::invalid_kind($sql, "query"); - } - } else { - if (!is_string($sql)) $sql = strval($sql); - if (_create::isa($sql)) { - $meta = ["isa" => "create", "type" => "ddl"]; - } elseif (_select::isa($sql)) { - $meta = ["isa" => "select", "type" => "dql"]; - } elseif (_insert::isa($sql)) { - $meta = ["isa" => "insert", "type" => "dml"]; - } elseif (_update::isa($sql)) { - $meta = ["isa" => "update", "type" => "dml"]; - } elseif (_delete::isa($sql)) { - $meta = ["isa" => "delete", "type" => "dml"]; - } elseif (_generic::isa($sql)) { - $meta = ["isa" => "generic", "type" => null]; - } else { - $meta = ["isa" => "generic", "type" => null]; - } - } - } - const DEBUG_QUERIES = false; function useStmt(\PDO $db, ?\PDOStatement &$stmt=null, ?string &$sql=null): bool { diff --git a/php/src/db/pgsql/Pgsql.php b/php/src/db/pgsql/Pgsql.php index 1f3dd1e..f45ba30 100644 --- a/php/src/db/pgsql/Pgsql.php +++ b/php/src/db/pgsql/Pgsql.php @@ -164,15 +164,27 @@ class Pgsql implements IDatabase { return $this->db; } - /** - * @return resource|false - */ - function _exec(string $query) { - return pg_query($this->db(), $query); + function _exec(string $query): bool { + $result = pg_query($this->db(), $query); + if ($result === false) return false; + pg_free_result($result); + return true; } function exec($query, ?array $params=null) { - // TODO: Implement exec() method. + $db = $this->db(); + $query = new query($query, $params); + $result = $query->_exec($db); + if ($query->isInsert()) { + $result = @pg_query($db, "select lastval()"); + if ($result === false) return false; + $lastInsertId = pg_fetch_row($result)[0]; + pg_free_result($result); + return $lastInsertId; + } + $affected_rows = pg_affected_rows($result); + pg_free_result($result); + return $affected_rows; } /** @var ITransactor[] */ @@ -243,14 +255,35 @@ class Pgsql implements IDatabase { } function get($query, ?array $params=null, bool $entireRow=false) { - // TODO: Implement get() method. + $db = $this->db(); + $query = new query($query, $params); + $result = $query->_exec($db); + $row = pg_fetch_assoc($result); + pg_free_result($result); + if ($row === false) return null; + $this->verifixRow($row); + if ($entireRow) return $row; + else return cl::first($row); } function one($query, ?array $params=null): ?array { - // TODO: Implement one() method. + return $this->get($query, $params, true); } function all($query, ?array $params=null, $primaryKeys=null): iterable { - // TODO: Implement all() method. + $db = $this->db(); + $query = new query($query, $params); + $result = $query->_exec($db); + $primaryKeys = cl::withn($primaryKeys); + while (($row = pg_fetch_assoc($result)) !== false) { + $this->verifixRow($row); + if ($primaryKeys !== null) { + $key = implode("-", cl::select($row, $primaryKeys)); + yield $key => $row; + } else { + yield $row; + } + } + pg_free_result($result); } } diff --git a/php/src/db/pgsql/PgsqlException.php b/php/src/db/pgsql/PgsqlException.php index 056bf41..afd21c4 100644 --- a/php/src/db/pgsql/PgsqlException.php +++ b/php/src/db/pgsql/PgsqlException.php @@ -6,6 +6,10 @@ use RuntimeException; use SQLite3; class PgsqlException extends RuntimeException { + static final function last_error($db): self { + return new static(pg_last_error($db)); + } + static final function wrap(Exception $e): self { return new static($e->getMessage(), $e->getCode(), $e); } diff --git a/php/src/db/pgsql/query.php b/php/src/db/pgsql/query.php new file mode 100644 index 0000000..c2b76b0 --- /dev/null +++ b/php/src/db/pgsql/query.php @@ -0,0 +1,36 @@ +sql); + //error_log(var_export($this->bindings, true)); + } + if ($this->bindings !== null) { + #XXX corriger les bindings et la requête *dans le constructeur* + $result = pg_query_params($db, $this->sql, $this->bindings); + } else { + $result = pg_query($db, $this->sql); + } + if ($result === false) throw PgsqlException::last_error($db); + return $result; + } +} diff --git a/php/src/db/sqlite/Sqlite.php b/php/src/db/sqlite/Sqlite.php index b797eb9..47ed23a 100644 --- a/php/src/db/sqlite/Sqlite.php +++ b/php/src/db/sqlite/Sqlite.php @@ -181,11 +181,6 @@ class Sqlite implements IDatabase { return $this->db()->exec($query); } - private static function is_insert(?string $sql): bool { - if ($sql === null) return false; - return preg_match('/^\s*insert\b/i', $sql); - } - function exec($query, ?array $params=null) { $db = $this->db(); $query = new query($query, $params); @@ -202,7 +197,7 @@ class Sqlite implements IDatabase { } else { $result = $db->exec($sql); if ($result === false) return false; - if (self::is_insert($sql)) return $db->lastInsertRowID(); + if ($query->isInsert()) return $db->lastInsertRowID(); else return $db->changes(); } } @@ -301,7 +296,7 @@ class Sqlite implements IDatabase { } protected function _fetchResult(SQLite3Result $result, ?SQLite3Stmt $stmt=null, $primaryKeys=null): Generator { - if ($primaryKeys !== null) $primaryKeys = cl::with($primaryKeys); + $primaryKeys = cl::withn($primaryKeys); try { while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) { $this->verifixRow($row); @@ -318,10 +313,6 @@ class Sqlite implements IDatabase { } } - /** - * si $primaryKeys est fourni, le résultat est indexé sur la(es) colonne(s) - * spécifiée(s) - */ function all($query, ?array $params=null, $primaryKeys=null): iterable { $db = $this->db(); $query = new query($query, $params); diff --git a/php/src/db/sqlite/query.php b/php/src/db/sqlite/query.php index 2c53dda..294d583 100644 --- a/php/src/db/sqlite/query.php +++ b/php/src/db/sqlite/query.php @@ -17,31 +17,6 @@ use SQLite3Stmt; class query extends _base { use Tbindings; - protected static function verifix(&$sql, ?array &$bindings=null, ?array &$meta=null): void { - if (is_array($sql)) { - $prefix = $sql[0] ?? null; - if ($prefix === null) { - throw new ValueException("requête invalide"); - } elseif (_create::isa($prefix)) { - $sql = _create::parse($sql, $bindings); - } elseif (_select::isa($prefix)) { - $sql = _select::parse($sql, $bindings); - } elseif (_insert::isa($prefix)) { - $sql = _insert::parse($sql, $bindings); - } elseif (_update::isa($prefix)) { - $sql = _update::parse($sql, $bindings); - } elseif (_delete::isa($prefix)) { - $sql = _delete::parse($sql, $bindings); - } elseif (_generic::isa($prefix)) { - $sql = _generic::parse($sql, $bindings); - } else { - throw SqliteException::wrap(ValueException::invalid_kind($sql, "query")); - } - } elseif (!is_string($sql)) { - $sql = strval($sql); - } - } - const DEBUG_QUERIES = false; function useStmt(SQLite3 $db, ?SQLite3Stmt &$stmt=null, ?string &$sql=null): bool {