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 {
 | 
			
		||||
    try {
 | 
			
		||||
      while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) {
 | 
			
		||||
 | 
			
		||||
@ -53,6 +53,120 @@ class _Query {
 | 
			
		||||
    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 = [
 | 
			
		||||
    "prefix" => "string",
 | 
			
		||||
    "table" => "string",
 | 
			
		||||
@ -77,7 +191,7 @@ class _Query {
 | 
			
		||||
    "having" => "?array",
 | 
			
		||||
  ];
 | 
			
		||||
  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 {
 | 
			
		||||
    # fusionner d'abord toutes les parties séquentielles
 | 
			
		||||
    $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 = [];
 | 
			
		||||
    if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix;
 | 
			
		||||
    # select
 | 
			
		||||
    ## select
 | 
			
		||||
    self::consume('select\s*', $tmpsql); $sql[] = "select";
 | 
			
		||||
    # cols
 | 
			
		||||
    ## cols
 | 
			
		||||
    $usercols = [];
 | 
			
		||||
    if (self::consume('(.*)\s*(?<=$|\bfrom\b)', $tmpsql, $ms)) {
 | 
			
		||||
    if (self::consume('(.*?)\s*(?=$|\bfrom\b)', $tmpsql, $ms)) {
 | 
			
		||||
      if ($ms[1]) $usercols[] = $ms[1];
 | 
			
		||||
    }
 | 
			
		||||
    $tmpcols = cl::withn($query["cols"] ?? null);
 | 
			
		||||
@ -119,24 +233,31 @@ class _Query {
 | 
			
		||||
        $usercols = array_merge($usercols, $cols);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (!$usercols && !$cols) $usercols = ["*"];
 | 
			
		||||
    $sql[] = implode(" ", $usercols);
 | 
			
		||||
    # from
 | 
			
		||||
    ## from
 | 
			
		||||
    $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];
 | 
			
		||||
      $sql[] = "from";
 | 
			
		||||
      $sql[] = $from;
 | 
			
		||||
    } elseif ($from !== null) {
 | 
			
		||||
      $sql[] = "from";
 | 
			
		||||
      $sql[] = $from;
 | 
			
		||||
    } else {
 | 
			
		||||
      throw new ValueException("expected table name: $usersql");
 | 
			
		||||
    }
 | 
			
		||||
    # where
 | 
			
		||||
    ## where
 | 
			
		||||
    $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];
 | 
			
		||||
    }
 | 
			
		||||
    $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
 | 
			
		||||
    # group by
 | 
			
		||||
    # having
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@ namespace nur\sery\db\sqlite;
 | 
			
		||||
 | 
			
		||||
use Exception;
 | 
			
		||||
use nulib\tests\TestCase;
 | 
			
		||||
use nur\sery\ValueException;
 | 
			
		||||
 | 
			
		||||
class SqliteTest extends TestCase {
 | 
			
		||||
  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"*/],
 | 
			
		||||
    ], 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"]]));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										86
									
								
								tests/db/sqlite/_QueryTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								tests/db/sqlite/_QueryTest.php
									
									
									
									
									
										Normal file
									
								
							@ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user