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