From 83d6fb6d16198e6dd96526f78e5599e2a3ea99e6 Mon Sep 17 00:00:00 2001 From: Sergiy Litvinchuk Date: Fri, 11 Nov 2016 19:08:47 +0200 Subject: [PATCH] fix bindings on update statements with advanced joins --- src/Illuminate/Database/Query/Builder.php | 4 +- .../Database/Query/Grammars/Grammar.php | 9 ++- .../Database/Query/Grammars/MySqlGrammar.php | 8 +-- .../Query/Grammars/PostgresGrammar.php | 19 +++++++ .../Query/Grammars/SqlServerGrammar.php | 19 +++++++ tests/Database/DatabaseQueryBuilderTest.php | 56 +++++++++++++++++++ 6 files changed, 105 insertions(+), 10 deletions(-) diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 25ba419c73bd..cc5ae7ad0b56 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -2160,12 +2160,10 @@ public function insertGetId(array $values, $sequence = null) */ public function update(array $values) { - $bindings = array_values(array_merge($values, $this->getBindings())); - $sql = $this->grammar->compileUpdate($this, $values); return $this->connection->update($sql, $this->cleanBindings( - $this->grammar->prepareBindingsForUpdate($bindings, $values) + $this->grammar->prepareBindingsForUpdate($this->bindings, $values) )); } diff --git a/src/Illuminate/Database/Query/Grammars/Grammar.php b/src/Illuminate/Database/Query/Grammars/Grammar.php index b10f74367e6e..81db7bdcbe02 100755 --- a/src/Illuminate/Database/Query/Grammars/Grammar.php +++ b/src/Illuminate/Database/Query/Grammars/Grammar.php @@ -5,6 +5,7 @@ use Illuminate\Database\Query\Builder; use Illuminate\Database\Query\JoinClause; use Illuminate\Database\Grammar as BaseGrammar; +use Illuminate\Support\Arr; class Grammar extends BaseGrammar { @@ -730,7 +731,13 @@ public function compileUpdate(Builder $query, $values) */ public function prepareBindingsForUpdate(array $bindings, array $values) { - return $bindings; + $bindingsWithoutJoin = Arr::except($bindings, 'join'); + + $preparedBindings = array_values( + array_merge($bindings['join'], $values, Arr::flatten($bindingsWithoutJoin)) + ); + + return $preparedBindings; } /** diff --git a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php index e5e32f873ef7..70069d0c55db 100755 --- a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php @@ -166,18 +166,14 @@ protected function compileJsonUpdateColumn($key, JsonExpression $value) */ public function prepareBindingsForUpdate(array $bindings, array $values) { - $index = 0; - foreach ($values as $column => $value) { if ($this->isJsonSelector($column) && in_array(gettype($value), ['boolean', 'integer', 'double'])) { - unset($bindings[$index]); + unset($values[$column]); } - - $index++; } - return $bindings; + return parent::prepareBindingsForUpdate($bindings, $values); } /** diff --git a/src/Illuminate/Database/Query/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Query/Grammars/PostgresGrammar.php index 041d6baf43d8..108aaf48bfa6 100755 --- a/src/Illuminate/Database/Query/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/PostgresGrammar.php @@ -4,6 +4,7 @@ use Illuminate\Support\Str; use Illuminate\Database\Query\Builder; +use Illuminate\Support\Arr; class PostgresGrammar extends Grammar { @@ -87,6 +88,24 @@ public function compileUpdate(Builder $query, $values) return trim("update {$table} set {$columns}{$from} $where"); } + /** + * Prepare the bindings for an update statement. + * + * @param array $bindings + * @param array $values + * @return array + */ + public function prepareBindingsForUpdate(array $bindings, array $values) + { + $bindingsWithoutJoin = Arr::except($bindings, 'join'); + + $preparedBindings = array_values( + array_merge($values, $bindings['join'], Arr::flatten($bindingsWithoutJoin)) + ); + + return $preparedBindings; + } + /** * Compile the columns for the update statement. * diff --git a/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php b/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php index 11093dd25a64..6bb604c23757 100755 --- a/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php @@ -3,6 +3,7 @@ namespace Illuminate\Database\Query\Grammars; use Illuminate\Database\Query\Builder; +use Illuminate\Support\Arr; class SqlServerGrammar extends Grammar { @@ -333,6 +334,24 @@ public function compileUpdate(Builder $query, $values) return trim("update {$table}{$joins} set $columns $where"); } + /** + * Prepare the bindings for an update statement. + * + * @param array $bindings + * @param array $values + * @return array + */ + public function prepareBindingsForUpdate(array $bindings, array $values) + { + $bindingsWithoutJoin = Arr::except($bindings, 'join'); + + $preparedBindings = array_values( + array_merge($values, $bindings['join'], Arr::flatten($bindingsWithoutJoin)) + ); + + return $preparedBindings; + } + /** * Wrap a table in keyword identifiers. * diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php index b2d857f4bf0d..91f9819cc25c 100755 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ b/tests/Database/DatabaseQueryBuilderTest.php @@ -1253,6 +1253,14 @@ public function testUpdateMethodWithJoins() $builder->getConnection()->shouldReceive('update')->once()->with('update "users" inner join "orders" on "users"."id" = "orders"."user_id" set "email" = ?, "name" = ? where "users"."id" = ?', ['foo', 'bar', 1])->andReturn(1); $result = $builder->from('users')->join('orders', 'users.id', '=', 'orders.user_id')->where('users.id', '=', 1)->update(['email' => 'foo', 'name' => 'bar']); $this->assertEquals(1, $result); + + $builder = $this->getBuilder(); + $builder->getConnection()->shouldReceive('update')->once()->with('update "users" inner join "orders" on "users"."id" = "orders"."user_id" and "users"."id" = ? set "email" = ?, "name" = ?', [1, 'foo', 'bar'])->andReturn(1); + $result = $builder->from('users')->join('orders', function ($join) { + $join->on('users.id', '=', 'orders.user_id') + ->where('users.id', '=', 1); + })->update(['email' => 'foo', 'name' => 'bar']); + $this->assertEquals(1, $result); } public function testUpdateMethodWithJoinsOnSqlServer() @@ -1261,6 +1269,46 @@ public function testUpdateMethodWithJoinsOnSqlServer() $builder->getConnection()->shouldReceive('update')->once()->with('update [users] set [email] = ?, [name] = ? from [users] inner join [orders] on [users].[id] = [orders].[user_id] where [users].[id] = ?', ['foo', 'bar', 1])->andReturn(1); $result = $builder->from('users')->join('orders', 'users.id', '=', 'orders.user_id')->where('users.id', '=', 1)->update(['email' => 'foo', 'name' => 'bar']); $this->assertEquals(1, $result); + + $builder = $this->getSqlServerBuilder(); + $builder->getConnection()->shouldReceive('update')->once()->with('update [users] set [email] = ?, [name] = ? from [users] inner join [orders] on [users].[id] = [orders].[user_id] and [users].[id] = ?', ['foo', 'bar', 1])->andReturn(1); + $result = $builder->from('users')->join('orders', function ($join) { + $join->on('users.id', '=', 'orders.user_id') + ->where('users.id', '=', 1); + })->update(['email' => 'foo', 'name' => 'bar']); + $this->assertEquals(1, $result); + } + + public function testUpdateMethodWithJoinsOnMySql() + { + $builder = $this->getMySqlBuilder(); + $builder->getConnection()->shouldReceive('update')->once()->with('update `users` inner join `orders` on `users`.`id` = `orders`.`user_id` set `email` = ?, `name` = ? where `users`.`id` = ?', ['foo', 'bar', 1])->andReturn(1); + $result = $builder->from('users')->join('orders', 'users.id', '=', 'orders.user_id')->where('users.id', '=', 1)->update(['email' => 'foo', 'name' => 'bar']); + $this->assertEquals(1, $result); + + $builder = $this->getMySqlBuilder(); + $builder->getConnection()->shouldReceive('update')->once()->with('update `users` inner join `orders` on `users`.`id` = `orders`.`user_id` and `users`.`id` = ? set `email` = ?, `name` = ?', [1, 'foo', 'bar'])->andReturn(1); + $result = $builder->from('users')->join('orders', function ($join) { + $join->on('users.id', '=', 'orders.user_id') + ->where('users.id', '=', 1); + })->update(['email' => 'foo', 'name' => 'bar']); + $this->assertEquals(1, $result); + } + + public function testUpdateMethodWithJoinsOnSQLite() + { + $builder = $this->getSQLiteBuilder(); + $builder->getConnection()->shouldReceive('update')->once()->with('update "users" inner join "orders" on "users"."id" = "orders"."user_id" set "email" = ?, "name" = ? where "users"."id" = ?', ['foo', 'bar', 1])->andReturn(1); + $result = $builder->from('users')->join('orders', 'users.id', '=', 'orders.user_id')->where('users.id', '=', 1)->update(['email' => 'foo', 'name' => 'bar']); + $this->assertEquals(1, $result); + + $builder = $this->getSQLiteBuilder(); + $builder->getConnection()->shouldReceive('update')->once()->with('update "users" inner join "orders" on "users"."id" = "orders"."user_id" and "users"."id" = ? set "email" = ?, "name" = ?', [1, 'foo', 'bar'])->andReturn(1); + $result = $builder->from('users')->join('orders', function ($join) { + $join->on('users.id', '=', 'orders.user_id') + ->where('users.id', '=', 1); + })->update(['email' => 'foo', 'name' => 'bar']); + $this->assertEquals(1, $result); } public function testUpdateMethodWithJoinsAndAliasesOnSqlServer() @@ -1285,6 +1333,14 @@ public function testUpdateMethodWithJoinsOnPostgres() $builder->getConnection()->shouldReceive('update')->once()->with('update "users" set "email" = ?, "name" = ? from "orders" where "users"."id" = ? and "users"."id" = "orders"."user_id"', ['foo', 'bar', 1])->andReturn(1); $result = $builder->from('users')->join('orders', 'users.id', '=', 'orders.user_id')->where('users.id', '=', 1)->update(['email' => 'foo', 'name' => 'bar']); $this->assertEquals(1, $result); + + $builder = $this->getPostgresBuilder(); + $builder->getConnection()->shouldReceive('update')->once()->with('update "users" set "email" = ?, "name" = ? from "orders" where "users"."id" = "orders"."user_id" and "users"."id" = ?', ['foo', 'bar', 1])->andReturn(1); + $result = $builder->from('users')->join('orders', function ($join) { + $join->on('users.id', '=', 'orders.user_id') + ->where('users.id', '=', 1); + })->update(['email' => 'foo', 'name' => 'bar']); + $this->assertEquals(1, $result); } public function testUpdateMethodRespectsRaw()