Skip to content

Commit

Permalink
Correctly escape single quotes in json paths
Browse files Browse the repository at this point in the history
  • Loading branch information
brendt committed Apr 10, 2019
1 parent e62dff8 commit 5167fc6
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 21 deletions.
2 changes: 2 additions & 0 deletions src/Illuminate/Database/Query/Grammars/Grammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,8 @@ protected function wrapJsonFieldAndPath($column)
*/
protected function wrapJsonPath($value, $delimiter = '->')
{
$value = preg_replace("/([\\\\]+)?\\'/", "\\'", $value);

return '\'$."'.str_replace($delimiter, '"."', $value).'"\'';
}

Expand Down
61 changes: 40 additions & 21 deletions tests/Database/DatabaseQueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1791,9 +1791,9 @@ public function testSubqueriesBindings()

$builder = $this->getBuilder()->select('*')->from('users')->where('email', '=', function ($q) {
$q->select(new Raw('max(id)'))
->from('users')->where('email', '=', 'bar')
->orderByRaw('email like ?', '%.com')
->groupBy('id')->having('id', '=', 4);
->from('users')->where('email', '=', 'bar')
->orderByRaw('email like ?', '%.com')
->groupBy('id')->having('id', '=', 4);
})->orWhere('id', '=', 'foo')->groupBy('id')->having('id', '=', 5);
$this->assertEquals([0 => 'bar', 1 => 4, 2 => '%.com', 3 => 'foo', 4 => 5], $builder->getBindings());
}
Expand Down Expand Up @@ -1977,7 +1977,7 @@ public function testUpdateMethodRespectsRaw()

public function testUpdateOrInsertMethod()
{
$builder = m::mock(Builder::class.'[where,exists,insert]', [
$builder = m::mock(Builder::class . '[where,exists,insert]', [
m::mock(ConnectionInterface::class),
new Grammar,
m::mock(Processor::class),
Expand All @@ -1989,7 +1989,7 @@ public function testUpdateOrInsertMethod()

$this->assertTrue($builder->updateOrInsert(['email' => 'foo'], ['name' => 'bar']));

$builder = m::mock(Builder::class.'[where,exists,update]', [
$builder = m::mock(Builder::class . '[where,exists,update]', [
m::mock(ConnectionInterface::class),
new Grammar,
m::mock(Processor::class),
Expand All @@ -2005,7 +2005,7 @@ public function testUpdateOrInsertMethod()

public function testUpdateOrInsertMethodWorksWithEmptyUpdateValues()
{
$builder = m::spy(Builder::class.'[where,exists,update]', [
$builder = m::spy(Builder::class . '[where,exists,update]', [
m::mock(ConnectionInterface::class),
new Grammar,
m::mock(Processor::class),
Expand Down Expand Up @@ -2136,11 +2136,11 @@ public function testMySqlUpdateWrappingJson()

$connection = $this->createMock(ConnectionInterface::class);
$connection->expects($this->once())
->method('update')
->with(
'update `users` set `name` = json_set(`name`, \'$."first_name"\', ?), `name` = json_set(`name`, \'$."last_name"\', ?) where `active` = ?',
['John', 'Doe', 1]
);
->method('update')
->with(
'update `users` set `name` = json_set(`name`, \'$."first_name"\', ?), `name` = json_set(`name`, \'$."last_name"\', ?) where `active` = ?',
['John', 'Doe', 1]
);

$builder = new Builder($connection, $grammar, $processor);

Expand All @@ -2154,11 +2154,11 @@ public function testMySqlUpdateWrappingNestedJson()

$connection = $this->createMock(ConnectionInterface::class);
$connection->expects($this->once())
->method('update')
->with(
'update `users` set `meta` = json_set(`meta`, \'$."name"."first_name"\', ?), `meta` = json_set(`meta`, \'$."name"."last_name"\', ?) where `active` = ?',
['John', 'Doe', 1]
);
->method('update')
->with(
'update `users` set `meta` = json_set(`meta`, \'$."name"."first_name"\', ?), `meta` = json_set(`meta`, \'$."name"."last_name"\', ?) where `active` = ?',
['John', 'Doe', 1]
);

$builder = new Builder($connection, $grammar, $processor);

Expand All @@ -2172,11 +2172,11 @@ public function testMySqlUpdateWithJsonPreparesBindingsCorrectly()

$connection = m::mock(ConnectionInterface::class);
$connection->shouldReceive('update')
->once()
->with(
'update `users` set `options` = json_set(`options`, \'$."enable"\', false), `updated_at` = ? where `id` = ?',
['2015-05-26 22:02:06', 0]
);
->once()
->with(
'update `users` set `options` = json_set(`options`, \'$."enable"\', false), `updated_at` = ? where `id` = ?',
['2015-05-26 22:02:06', 0]
);
$builder = new Builder($connection, $grammar, $processor);
$builder->from('users')->where('id', '=', 0)->update(['options->enable' => false, 'updated_at' => '2015-05-26 22:02:06']);

Expand Down Expand Up @@ -2252,6 +2252,25 @@ public function testMySqlWrappingJsonWithBooleanAndIntegerThatLooksLikeOne()
$this->assertEquals('select * from `users` where json_extract(`items`, \'$."available"\') = true and json_extract(`items`, \'$."active"\') = false and json_unquote(json_extract(`items`, \'$."number_available"\')) = ?', $builder->toSql());
}

public function testJsonPathEscaping()
{
$expectedJsonEscape = <<<SQL
select json_unquote(json_extract(`json`, '$."\'))#"'))
SQL;

$builder = $this->getMySqlBuilder();
$builder->select("json->'))#");
$this->assertEquals($expectedJsonEscape, $builder->toSql());

$builder = $this->getMySqlBuilder();
$builder->select("json->\'))#");
$this->assertEquals($expectedJsonEscape, $builder->toSql());

$builder = $this->getMySqlBuilder();
$builder->select("json->\\\'))#");
$this->assertEquals($expectedJsonEscape, $builder->toSql());
}

public function testMySqlWrappingJson()
{
$builder = $this->getMySqlBuilder();
Expand Down

0 comments on commit 5167fc6

Please sign in to comment.