2022-08-27 18:07:31 +00:00
|
|
|
<?php declare(strict_types=1);
|
2009-08-27 12:48:09 +00:00
|
|
|
|
2014-01-28 12:38:06 +00:00
|
|
|
/*
|
2022-02-06 20:37:02 +00:00
|
|
|
This file is a part of myTinyTodo.
|
|
|
|
|
(C) Copyright 2009,2019-2022 Max Pozdeev <maxpozdeev@gmail.com>
|
|
|
|
|
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
|
2014-01-28 12:38:06 +00:00
|
|
|
*/
|
2009-10-22 08:29:01 +00:00
|
|
|
|
2021-07-26 16:13:57 +00:00
|
|
|
class DatabaseResult_Sqlite3 extends DatabaseResult_Abstract
|
2009-08-27 12:48:09 +00:00
|
|
|
{
|
2022-02-06 20:37:02 +00:00
|
|
|
/** @var PDOStatement */
|
2022-02-07 15:22:59 +00:00
|
|
|
protected $q;
|
2022-08-27 18:07:31 +00:00
|
|
|
|
|
|
|
|
/** @var int */
|
2022-02-07 15:22:59 +00:00
|
|
|
protected $affected;
|
2022-02-06 20:37:02 +00:00
|
|
|
|
2022-08-27 18:07:31 +00:00
|
|
|
function __construct(PDO $dbh, string $query, bool $resultless = false)
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
|
|
|
|
// use with DELETE, INSERT, UPDATE
|
|
|
|
|
if ($resultless)
|
|
|
|
|
{
|
2022-08-27 18:07:31 +00:00
|
|
|
$this->affected = (int) $dbh->exec($query); //throws PDOException
|
2022-02-06 20:37:02 +00:00
|
|
|
}
|
|
|
|
|
// SELECT
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
$this->q = $dbh->query($query); //throws PDOException
|
|
|
|
|
$this->affected = $this->q->rowCount();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-27 18:07:31 +00:00
|
|
|
function fetchRow(): ?array
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
2022-08-27 18:07:31 +00:00
|
|
|
$res = $this->q->fetch(PDO::FETCH_NUM);
|
|
|
|
|
if ($res === false || !is_array($res)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return $res;
|
2022-02-06 20:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
2022-08-27 18:07:31 +00:00
|
|
|
function fetchAssoc(): ?array
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
2022-08-27 18:07:31 +00:00
|
|
|
$res = $this->q->fetch(PDO::FETCH_ASSOC);
|
|
|
|
|
if ($res === false || !is_array($res)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return $res;
|
2022-02-06 20:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
2022-08-27 18:07:31 +00:00
|
|
|
function rowsAffected(): int
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
|
|
|
|
return $this->affected;
|
|
|
|
|
}
|
2021-07-24 19:48:30 +00:00
|
|
|
|
2009-08-27 12:48:09 +00:00
|
|
|
}
|
|
|
|
|
|
2021-07-26 16:13:57 +00:00
|
|
|
class Database_Sqlite3 extends Database_Abstract
|
2009-08-27 12:48:09 +00:00
|
|
|
{
|
2022-02-06 20:37:02 +00:00
|
|
|
/** @var PDO */
|
2022-02-07 15:22:59 +00:00
|
|
|
protected $dbh;
|
2022-08-27 18:07:31 +00:00
|
|
|
|
|
|
|
|
/** @var int */
|
|
|
|
|
protected $affected = 0;
|
2022-02-06 20:37:02 +00:00
|
|
|
|
|
|
|
|
function __construct()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-27 18:07:31 +00:00
|
|
|
function connect(array $params): void
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
|
|
|
|
$filename = $params['filename'];
|
|
|
|
|
$options = array(
|
|
|
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
|
|
|
|
|
);
|
|
|
|
|
$this->dbh = new PDO("sqlite:$filename", null, null, $options); //throws PDOException
|
2022-11-10 18:24:41 +00:00
|
|
|
$this->dbh->sqliteCreateFunction('utf8_lower', [$this, 'utf8lower'], 1);
|
|
|
|
|
$this->dbh->sqliteCreateCollation('UTF8CI', [$this, 'utf8ci']);
|
2022-02-06 20:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
SELECT queries for single row
|
|
|
|
|
*/
|
2022-08-27 18:07:31 +00:00
|
|
|
function sq(string $query, ?array $values = null)
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
2022-08-27 18:07:31 +00:00
|
|
|
$q = $this->_dq($query, $values);
|
2022-02-06 20:37:02 +00:00
|
|
|
|
|
|
|
|
$res = $q->fetchRow();
|
2022-08-27 18:07:31 +00:00
|
|
|
if ($res === false || !is_array($res)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2022-02-06 20:37:02 +00:00
|
|
|
|
|
|
|
|
if (sizeof($res) > 1) return $res;
|
|
|
|
|
else return $res[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2022-08-27 18:07:31 +00:00
|
|
|
Returns single row of SELECT query as dictionary array (FETCH_ASSOC).
|
2022-02-06 20:37:02 +00:00
|
|
|
*/
|
2022-08-27 18:07:31 +00:00
|
|
|
function sqa(string $query, ?array $values = null): ?array
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
2022-08-27 18:07:31 +00:00
|
|
|
$q = $this->_dq($query, $values);
|
2022-02-06 20:37:02 +00:00
|
|
|
$res = $q->fetchAssoc();
|
2022-08-27 18:07:31 +00:00
|
|
|
if ($res === false || !is_array($res)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2022-02-06 20:37:02 +00:00
|
|
|
return $res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
SELECT queries for multiple rows
|
|
|
|
|
*/
|
2022-08-27 18:07:31 +00:00
|
|
|
function dq(string $query, ?array $values = null) : DatabaseResult_Abstract
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
2022-08-27 18:07:31 +00:00
|
|
|
return $this->_dq($query, $values);
|
2022-02-06 20:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
for resultless queries like INSERT,UPDATE,DELETE
|
|
|
|
|
*/
|
2022-08-27 18:07:31 +00:00
|
|
|
function ex(string $query, ?array $values = null): void
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
2022-08-27 18:07:31 +00:00
|
|
|
$this->_dq($query, $values, true);
|
2022-02-06 20:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
2022-08-27 18:07:31 +00:00
|
|
|
private function _dq(string $query, ?array $values = null, bool $resultless = false) : DatabaseResult_Abstract
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
2022-08-27 18:07:31 +00:00
|
|
|
if (null !== $values && sizeof($values) > 0)
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
2022-08-27 18:07:31 +00:00
|
|
|
$m = explode('?', $query);
|
|
|
|
|
if (sizeof($m) < sizeof($values)+1) {
|
2022-02-06 20:37:02 +00:00
|
|
|
throw new Exception("params to set MORE than query params");
|
|
|
|
|
}
|
2022-08-27 18:07:31 +00:00
|
|
|
if (sizeof($m) > sizeof($values)+1) {
|
2022-02-06 20:37:02 +00:00
|
|
|
throw new Exception("params to set LESS than query params");
|
|
|
|
|
}
|
|
|
|
|
$query = "";
|
|
|
|
|
for ($i=0; $i<sizeof($m)-1; $i++) {
|
2022-08-27 18:07:31 +00:00
|
|
|
$query .= $m[$i]. $this->quote($values[$i]);
|
2022-02-06 20:37:02 +00:00
|
|
|
}
|
|
|
|
|
$query .= $m[$i];
|
|
|
|
|
}
|
|
|
|
|
$this->lastQuery = $query;
|
|
|
|
|
$dbr = new DatabaseResult_Sqlite3($this->dbh, $query, $resultless);
|
|
|
|
|
$this->affected = $dbr->rowsAffected();
|
|
|
|
|
return $dbr;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-27 18:07:31 +00:00
|
|
|
function affected(): int
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
|
|
|
|
return $this->affected;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-27 18:07:31 +00:00
|
|
|
function quote($value): string
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
2022-08-27 18:07:31 +00:00
|
|
|
if (null === $value) {
|
|
|
|
|
return 'null';
|
|
|
|
|
}
|
|
|
|
|
return $this->dbh->quote( (string) $value);
|
2022-02-06 20:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
2022-08-27 18:07:31 +00:00
|
|
|
function quoteForLike(string $format, string $string): string
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
2022-08-27 18:07:31 +00:00
|
|
|
$string = str_replace(array('\\','%','_'), array('\\\\','\%','\_'), $string);
|
|
|
|
|
return $this->dbh->quote(sprintf($format, $string)). " ESCAPE '\'";
|
2022-02-06 20:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
2022-11-10 18:24:41 +00:00
|
|
|
/**
|
|
|
|
|
* Produce case-insensitive like
|
|
|
|
|
*/
|
|
|
|
|
function like(string $column, string $format, string $string): string
|
|
|
|
|
{
|
|
|
|
|
$column = str_replace('"', '""', $column);
|
|
|
|
|
return 'utf8_lower("'. $column. '") LIKE '. $this->quoteForLike($format, mb_strtolower($string, 'UTF-8'));
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-27 18:07:31 +00:00
|
|
|
function lastInsertId(?string $name = null): ?string
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
2022-08-27 18:07:31 +00:00
|
|
|
$ret = $this->dbh->lastInsertId();
|
|
|
|
|
if (false === $ret) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return (string) $ret;
|
2022-02-06 20:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
2022-08-27 18:07:31 +00:00
|
|
|
function tableExists(string $table): bool
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
2022-08-27 18:07:31 +00:00
|
|
|
$exists = $this->sq("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", [$table]);
|
2022-02-06 20:37:02 +00:00
|
|
|
if ($exists == "1") {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-08-27 18:07:31 +00:00
|
|
|
$exists = $this->sq("SELECT 1 FROM sqlite_temp_master WHERE type='table' AND name=?", [$table]);
|
2022-02-06 20:37:02 +00:00
|
|
|
if ($exists == "1") {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-27 18:07:31 +00:00
|
|
|
function tableFieldExists(string $table, string $field): bool
|
2022-02-06 20:37:02 +00:00
|
|
|
{
|
|
|
|
|
$q = $this->dq("PRAGMA table_info(". $this->quote($table). ")");
|
|
|
|
|
while ($r = $q->fetchRow()) {
|
|
|
|
|
if ($r[1] == $field) return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-11-10 18:24:41 +00:00
|
|
|
|
|
|
|
|
public function utf8lower($value)
|
|
|
|
|
{
|
|
|
|
|
if (is_null($value)) return '';
|
|
|
|
|
return mb_strtolower((string)$value, 'UTF-8');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function utf8ci(string $str1, string $str2): int
|
|
|
|
|
{
|
|
|
|
|
return strcmp(mb_strtolower($str1, 'UTF-8'), mb_strtolower($str2, 'UTF-8'));
|
|
|
|
|
}
|
2009-08-27 12:48:09 +00:00
|
|
|
}
|