diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3115f0a0..0621a2cc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,7 +64,7 @@ Simply run: docker-compose up -d ``` -Then, the necessary services will be available and the tests should pass (although you may need to install PHP memcache extensions in a separate step, see below ). +Then, the necessary services will be available and the tests should pass (although you may need to install PHP memcache extensions in a separate step, see below). When you're done, you can take it down with: ```sh diff --git a/lib/Adapter/PgsqlAdapter.php b/lib/Adapter/PgsqlAdapter.php index 4915a3fb..cbf434c3 100644 --- a/lib/Adapter/PgsqlAdapter.php +++ b/lib/Adapter/PgsqlAdapter.php @@ -135,4 +135,23 @@ public function set_encoding(string $charset): void { $this->query("SET NAMES '$charset'"); } + + /** + * @see Connection::escapeColumns() + * + * @param string $expression The where clause to be escaped + * @param list $columns The columns of the table + */ + public function escapeColumns(string $expression, array $columns): string + { + static $quotedNames = []; + foreach ($columns as $column) { + if ($column !== strtolower($column)) { + $quotedNames[$column] ??= $this->quote_name($column); + $expression = str_replace($column, $this->quote_name($column), $expression); + } + } + + return $expression; + } } diff --git a/lib/Connection.php b/lib/Connection.php index 03f85728..5be9a7ef 100644 --- a/lib/Connection.php +++ b/lib/Connection.php @@ -485,15 +485,24 @@ public function next_sequence_value(string $sequence_name): ?string * Quote a name like table names and field names. * * @param string $string string to quote - * - * @return string */ - public function quote_name($string) + public function quote_name($string): string { return $string[0] === static::$QUOTE_CHARACTER || $string[strlen($string) - 1] === static::$QUOTE_CHARACTER ? $string : static::$QUOTE_CHARACTER . $string . static::$QUOTE_CHARACTER; } + /** + * Escape the column names in the where phrases + * + * @param string $expression The where clause to be escaped + * @param list $columns The columns of the table + */ + public function escapeColumns(string $expression, array $columns): string + { + return $expression; + } + public function date_string(\DateTimeInterface $datetime): string { return $datetime->format(static::$date_format); diff --git a/lib/SQLBuilder.php b/lib/SQLBuilder.php index 067f544e..577c9418 100644 --- a/lib/SQLBuilder.php +++ b/lib/SQLBuilder.php @@ -118,18 +118,20 @@ public function get_where_values(): array /** * @param list $clauses * @param array $mappedNames + * @param array $columns Table column names * * @throws Exception\ExpressionsException * * @return $this */ - public function where(array $clauses=[], array $mappedNames=[]): static + public function where(array $clauses=[], array $mappedNames=[], array $columns=[]): static { $values = []; $sql = ''; $glue = ' AND '; foreach ($clauses as $idx => $clause) { $expression = $clause->to_s($this->connection, !empty($this->joins) ? $this->table : '', $mappedNames); + $expression = $this->connection->escapeColumns($expression, $columns); $values = array_merge($values, array_flatten($clause->values())); $inverse = $clause->negated() ? '!' : ''; $wrappedExpression = $inverse || count($clauses) > 1 ? '(' . $expression . ')' : $expression; diff --git a/lib/Table.php b/lib/Table.php index 45aef308..232a96e7 100644 --- a/lib/Table.php +++ b/lib/Table.php @@ -229,7 +229,7 @@ public function options_to_sql(array $options): SQLBuilder ); } - $sql->where($options['conditions'] ?? [], $options['mapped_names'] ?? []); + $sql->where($options['conditions'] ?? [], $options['mapped_names'] ?? [], array_keys($this->columns)); if (array_key_exists('order', $options)) { $sql->order($options['order']); diff --git a/lib/WhereClause.php b/lib/WhereClause.php index 5a72f924..8d7e8efd 100644 --- a/lib/WhereClause.php +++ b/lib/WhereClause.php @@ -218,8 +218,6 @@ private function build_sql_from_hash(Connection $connection, array $hash, string $table = !empty($prependTableName) ? $connection->quote_name($prependTableName) : ''; foreach ($hash as $name => $value) { - $name = $connection->quote_name($name); - if (!empty($prependTableName)) { $name = $table . '.' . $name; } diff --git a/test/ActiveRecordNotTest.php b/test/ActiveRecordNotTest.php index 3bdddcf2..a15eae93 100644 --- a/test/ActiveRecordNotTest.php +++ b/test/ActiveRecordNotTest.php @@ -23,8 +23,8 @@ public static function conditions(): array 'hash' => [[ 'name' => 'Another Book', ], - 'WHERE `name` = ?', - 'WHERE !(`name` = ?)' + 'WHERE name = ?', + 'WHERE !(name = ?)' ], 'in' => [[ 'book_id in (?)', [1, 2], diff --git a/test/PgsqlAdapterTest.php b/test/PgsqlAdapterTest.php index d9b460b5..6e8954d4 100644 --- a/test/PgsqlAdapterTest.php +++ b/test/PgsqlAdapterTest.php @@ -13,6 +13,15 @@ public function testInsertId() $this->assertTrue($this->connection->insert_id('authors_author_id_seq') > 0); } + public function testToSql(): void + { + $this->assertEquals( + 'SELECT * FROM "authors" WHERE "mixedCaseField" = ? ORDER BY name', + \test\models\Author::where('mixedCaseField = ?', 'The Art of Main Tanking') + ->order('name')->to_sql() + ); + } + public function testInsertIdWithParams() { $x = ['name']; diff --git a/test/WhereClauseTest.php b/test/WhereClauseTest.php index 6725af7f..95b350e5 100644 --- a/test/WhereClauseTest.php +++ b/test/WhereClauseTest.php @@ -1,25 +1,10 @@ connection = ConnectionManager::get_connection(); - } catch (DatabaseException $e) { - $this->markTestSkipped('failed to connect. ' . $e->getMessage()); - } - } - public function testValues() { $c = new WhereClause('a=? and b=?', [1, 2]); @@ -134,7 +119,7 @@ public function testSubstituteOnString(): void public function testSubstituteOnHash(): void { $a = new WhereClause(['name' => 'Tito', 'id'=> 1]); - $this->assertEquals("`name` = 'Tito' AND `id` = 1", $a->to_s($this->connection, substitute: true)); + $this->assertEquals("name = 'Tito' AND id = 1", $a->to_s($this->connection, substitute: true)); } public function testSubstituteQuotesScalarsButNotOthers(): void @@ -199,7 +184,7 @@ public function testNullValue(): void public function testHashWithDefaultGlue(): void { $a = new WhereClause(['id' => 1, 'name' => 'Tito']); - $this->assertEquals('`id` = ? AND `name` = ?', $a->to_s($this->connection)); + $this->assertEquals('id = ? AND name = ?', $a->to_s($this->connection)); } public function testHashWithGlue(): void @@ -208,7 +193,7 @@ public function testHashWithGlue(): void 'id' => 1, 'name' => 'Tito' ]); - $this->assertEquals('`id` = ?, `name` = ?', $a->to_s($this->connection, glue: ', ')); + $this->assertEquals('id = ?, name = ?', $a->to_s($this->connection, glue: ', ')); } public function testHashWithArray(): void @@ -217,6 +202,6 @@ public function testHashWithArray(): void 'id' => 1, 'name' => ['Tito', 'Mexican'] ]); - $this->assertEquals('`id` = ? AND `name` IN(?,?)', $a->to_s($this->connection)); + $this->assertEquals('id = ? AND name IN(?,?)', $a->to_s($this->connection)); } }