From 8fffc93dc232dee7de10fd3784dab8ef831c5c97 Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Sun, 18 Sep 2022 11:09:04 -0400 Subject: [PATCH 1/7] Implement compileJsonCast method in database query grammar --- src/Illuminate/Database/Query/Grammars/Grammar.php | 11 +++++++++++ .../Database/Query/Grammars/MySqlGrammar.php | 11 +++++++++++ .../Database/Query/Grammars/SqlServerGrammar.php | 11 +++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/Illuminate/Database/Query/Grammars/Grammar.php b/src/Illuminate/Database/Query/Grammars/Grammar.php index 86c705fedd98..6f7578820b5f 100755 --- a/src/Illuminate/Database/Query/Grammars/Grammar.php +++ b/src/Illuminate/Database/Query/Grammars/Grammar.php @@ -611,6 +611,17 @@ protected function compileJsonContains($column, $value) throw new RuntimeException('This database engine does not support JSON contains operations.'); } + /** + * Compile a "JSON cast" statement into SQL. + * + * @param string $value + * @return string + */ + public function compileJsonCast($value) + { + return $value; + } + /** * Prepare the binding for a "JSON contains" statement. * diff --git a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php index 7586ce825bf4..8d4be8d7e803 100755 --- a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php @@ -113,6 +113,17 @@ protected function compileJsonContainsKey($column) return 'ifnull(json_contains_path('.$field.', \'one\''.$path.'), 0)'; } + /** + * Compile a "JSON cast" statement into SQL. + * + * @param string $value + * @return string + */ + public function compileJsonCast($value) + { + return "cast($value as json)"; + } + /** * Compile a "JSON length" statement into SQL. * diff --git a/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php b/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php index a26157ea84a3..cb4867771c53 100755 --- a/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php @@ -154,6 +154,17 @@ protected function compileJsonContains($column, $value) return $value.' in (select [value] from openjson('.$field.$path.'))'; } + /** + * Compile a "JSON cast" statement into SQL. + * + * @param string $value + * @return string + */ + public function compileJsonCast($value) + { + return "json_query($value)"; + } + /** * Prepare the binding for a "JSON contains" statement. * From 33709564722617bb96466f0ae8c19d87d67481a1 Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Sun, 18 Sep 2022 11:11:40 -0400 Subject: [PATCH 2/7] Use compileJsonCast to retrieve queryable json value --- .../Foundation/Testing/Concerns/InteractsWithDatabase.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php index 95a155c95b45..4235175f069f 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php @@ -180,7 +180,9 @@ public function castAsJson($value) $value = DB::connection()->getPdo()->quote($value); - return DB::raw("CAST($value AS JSON)"); + return DB::raw( + DB::connection()->getQueryGrammar()->compileJsonCast($value) + ); } /** From 9ae3f0214c18b6b1921e73fcf10056bf54bad8ae Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Sun, 18 Sep 2022 12:07:04 -0400 Subject: [PATCH 3/7] Add castAsJson tests --- .../Concerns/InteractsWithDatabaseTest.php | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 tests/Testing/Concerns/InteractsWithDatabaseTest.php diff --git a/tests/Testing/Concerns/InteractsWithDatabaseTest.php b/tests/Testing/Concerns/InteractsWithDatabaseTest.php new file mode 100644 index 000000000000..c32c4f7f38e8 --- /dev/null +++ b/tests/Testing/Concerns/InteractsWithDatabaseTest.php @@ -0,0 +1,139 @@ +assertEquals(<<castAsJson(['foo', 'bar'], $grammar) + ); + + $this->assertEquals(<<castAsJson(collect(['foo', 'bar']), $grammar) + ); + + $this->assertEquals(<<castAsJson((object) ['foo' => 'bar'], $grammar) + ); + } + + public function testCastToJsonPostgres() + { + $grammar = new PostgresGrammar(); + + $this->assertEquals(<<castAsJson(['foo', 'bar'], $grammar) + ); + + $this->assertEquals(<<castAsJson(collect(['foo', 'bar']), $grammar) + ); + + $this->assertEquals(<<castAsJson((object) ['foo' => 'bar'], $grammar) + ); + } + + public function testCastToJsonSqlServer() + { + $grammar = new SqlServerGrammar(); + + $this->assertEquals(<<castAsJson(['foo', 'bar'], $grammar) + ); + + $this->assertEquals(<<castAsJson(collect(['foo', 'bar']), $grammar) + ); + + $this->assertEquals(<<castAsJson((object) ['foo' => 'bar'], $grammar) + ); + } + + public function testCastToJsonMySql() + { + $grammar = new MySqlGrammar(); + + $this->assertEquals(<<castAsJson(['foo', 'bar'], $grammar) + ); + + $this->assertEquals(<<castAsJson(collect(['foo', 'bar']), $grammar) + ); + + $this->assertEquals(<<castAsJson((object) ['foo' => 'bar'], $grammar) + ); + } + + protected function castAsJson($value, $grammar) + { + $connection = m::mock(ConnectionInterface::class); + + $connection->shouldReceive('getQueryGrammar')->once()->andReturn($grammar); + + $connection->shouldReceive('getPdo->quote')->once()->andReturnUsing(function ($value) { + return "'".$value."'"; + }); + + DB::shouldReceive('connection')->once()->andReturn($connection); + + DB::shouldReceive('raw')->once()->andReturnUsing(function ($value) { + return new Expression($value); + }); + + $instance = new class + { + use InteractsWithDatabase; + }; + + return $instance->castAsJson($value)->getValue(); + } +} From 08cac9e2d1d25f3bc10e3cb32442d0cf577efb65 Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Sun, 18 Sep 2022 12:27:23 -0400 Subject: [PATCH 4/7] CS fixes --- .../Concerns/InteractsWithDatabaseTest.php | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/Testing/Concerns/InteractsWithDatabaseTest.php b/tests/Testing/Concerns/InteractsWithDatabaseTest.php index c32c4f7f38e8..06912e468730 100644 --- a/tests/Testing/Concerns/InteractsWithDatabaseTest.php +++ b/tests/Testing/Concerns/InteractsWithDatabaseTest.php @@ -25,19 +25,19 @@ public function testCastToJsonSqlite() { $grammar = new SQLiteGrammar(); - $this->assertEquals(<<assertEquals(<<<'TEXT' '["foo","bar"]' TEXT, $this->castAsJson(['foo', 'bar'], $grammar) ); - $this->assertEquals(<<assertEquals(<<<'TEXT' '["foo","bar"]' TEXT, $this->castAsJson(collect(['foo', 'bar']), $grammar) ); - $this->assertEquals(<<assertEquals(<<<'TEXT' '{"foo":"bar"}' TEXT, $this->castAsJson((object) ['foo' => 'bar'], $grammar) @@ -48,19 +48,19 @@ public function testCastToJsonPostgres() { $grammar = new PostgresGrammar(); - $this->assertEquals(<<assertEquals(<<<'TEXT' '["foo","bar"]' TEXT, $this->castAsJson(['foo', 'bar'], $grammar) ); - $this->assertEquals(<<assertEquals(<<<'TEXT' '["foo","bar"]' TEXT, $this->castAsJson(collect(['foo', 'bar']), $grammar) ); - $this->assertEquals(<<assertEquals(<<<'TEXT' '{"foo":"bar"}' TEXT, $this->castAsJson((object) ['foo' => 'bar'], $grammar) @@ -71,19 +71,19 @@ public function testCastToJsonSqlServer() { $grammar = new SqlServerGrammar(); - $this->assertEquals(<<assertEquals(<<<'TEXT' json_query('["foo","bar"]') TEXT, $this->castAsJson(['foo', 'bar'], $grammar) ); - $this->assertEquals(<<assertEquals(<<<'TEXT' json_query('["foo","bar"]') TEXT, $this->castAsJson(collect(['foo', 'bar']), $grammar) ); - $this->assertEquals(<<assertEquals(<<<'TEXT' json_query('{"foo":"bar"}') TEXT, $this->castAsJson((object) ['foo' => 'bar'], $grammar) @@ -94,19 +94,19 @@ public function testCastToJsonMySql() { $grammar = new MySqlGrammar(); - $this->assertEquals(<<assertEquals(<<<'TEXT' cast('["foo","bar"]' as json) TEXT, $this->castAsJson(['foo', 'bar'], $grammar) ); - $this->assertEquals(<<assertEquals(<<<'TEXT' cast('["foo","bar"]' as json) TEXT, $this->castAsJson(collect(['foo', 'bar']), $grammar) ); - $this->assertEquals(<<assertEquals(<<<'TEXT' cast('{"foo":"bar"}' as json) TEXT, $this->castAsJson((object) ['foo' => 'bar'], $grammar) @@ -115,10 +115,10 @@ public function testCastToJsonMySql() protected function castAsJson($value, $grammar) { - $connection = m::mock(ConnectionInterface::class); + $connection = m::mock(ConnectionInterface::class); $connection->shouldReceive('getQueryGrammar')->once()->andReturn($grammar); - + $connection->shouldReceive('getPdo->quote')->once()->andReturnUsing(function ($value) { return "'".$value."'"; }); From 8a02d568192b1ba16e48c4fc3b29bc0e44e51a0d Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Sun, 18 Sep 2022 12:36:53 -0400 Subject: [PATCH 5/7] Fix tests --- .../Concerns/InteractsWithDatabaseTest.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/Testing/Concerns/InteractsWithDatabaseTest.php b/tests/Testing/Concerns/InteractsWithDatabaseTest.php index 06912e468730..750203d2d891 100644 --- a/tests/Testing/Concerns/InteractsWithDatabaseTest.php +++ b/tests/Testing/Concerns/InteractsWithDatabaseTest.php @@ -16,9 +16,15 @@ class InteractsWithDatabaseTest extends TestCase { - protected function tearDown(): void + protected function setUp(): void { Facade::clearResolvedInstances(); + Facade::setFacadeApplication(null); + } + + protected function tearDown(): void + { + m::close(); } public function testCastToJsonSqlite() @@ -117,15 +123,15 @@ protected function castAsJson($value, $grammar) { $connection = m::mock(ConnectionInterface::class); - $connection->shouldReceive('getQueryGrammar')->once()->andReturn($grammar); + $connection->shouldReceive('getQueryGrammar')->andReturn($grammar); - $connection->shouldReceive('getPdo->quote')->once()->andReturnUsing(function ($value) { + $connection->shouldReceive('getPdo->quote')->andReturnUsing(function ($value) { return "'".$value."'"; }); - DB::shouldReceive('connection')->once()->andReturn($connection); + DB::shouldReceive('connection')->andReturn($connection); - DB::shouldReceive('raw')->once()->andReturnUsing(function ($value) { + DB::shouldReceive('raw')->andReturnUsing(function ($value) { return new Expression($value); }); From cf85311498ad75b9fffa64197487e88cefef8226 Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Sun, 18 Sep 2022 21:54:06 -0400 Subject: [PATCH 6/7] Use single quotes with concatenation for consistency --- src/Illuminate/Database/Query/Grammars/MySqlGrammar.php | 2 +- src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php index 8d4be8d7e803..80347d2c4c80 100755 --- a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php @@ -121,7 +121,7 @@ protected function compileJsonContainsKey($column) */ public function compileJsonCast($value) { - return "cast($value as json)"; + return 'cast('.$value.' as json)'; } /** diff --git a/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php b/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php index cb4867771c53..153fe1be5e9c 100755 --- a/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php @@ -162,7 +162,7 @@ protected function compileJsonContains($column, $value) */ public function compileJsonCast($value) { - return "json_query($value)"; + return 'json_query('.$value.')'; } /** From 55374e7cfada9b31d8d35d73fb49d6389a77fb6a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 19 Sep 2022 14:48:52 -0500 Subject: [PATCH 7/7] formatting --- .../Database/Query/Grammars/Grammar.php | 22 +++++++++---------- .../Database/Query/Grammars/MySqlGrammar.php | 20 ++++++++--------- .../Query/Grammars/SqlServerGrammar.php | 22 +++++++++---------- .../Concerns/InteractsWithDatabase.php | 2 +- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/Illuminate/Database/Query/Grammars/Grammar.php b/src/Illuminate/Database/Query/Grammars/Grammar.php index 6f7578820b5f..46fae45a02d0 100755 --- a/src/Illuminate/Database/Query/Grammars/Grammar.php +++ b/src/Illuminate/Database/Query/Grammars/Grammar.php @@ -611,17 +611,6 @@ protected function compileJsonContains($column, $value) throw new RuntimeException('This database engine does not support JSON contains operations.'); } - /** - * Compile a "JSON cast" statement into SQL. - * - * @param string $value - * @return string - */ - public function compileJsonCast($value) - { - return $value; - } - /** * Prepare the binding for a "JSON contains" statement. * @@ -693,6 +682,17 @@ protected function compileJsonLength($column, $operator, $value) throw new RuntimeException('This database engine does not support JSON length operations.'); } + /** + * Compile a "JSON value cast" statement into SQL. + * + * @param string $value + * @return string + */ + public function compileJsonValueCast($value) + { + return $value; + } + /** * Compile a "where fulltext" clause. * diff --git a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php index 80347d2c4c80..3e4f06a20bef 100755 --- a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php @@ -114,29 +114,29 @@ protected function compileJsonContainsKey($column) } /** - * Compile a "JSON cast" statement into SQL. + * Compile a "JSON length" statement into SQL. * + * @param string $column + * @param string $operator * @param string $value * @return string */ - public function compileJsonCast($value) + protected function compileJsonLength($column, $operator, $value) { - return 'cast('.$value.' as json)'; + [$field, $path] = $this->wrapJsonFieldAndPath($column); + + return 'json_length('.$field.$path.') '.$operator.' '.$value; } /** - * Compile a "JSON length" statement into SQL. + * Compile a "JSON value cast" statement into SQL. * - * @param string $column - * @param string $operator * @param string $value * @return string */ - protected function compileJsonLength($column, $operator, $value) + public function compileJsonValueCast($value) { - [$field, $path] = $this->wrapJsonFieldAndPath($column); - - return 'json_length('.$field.$path.') '.$operator.' '.$value; + return 'cast('.$value.' as json)'; } /** diff --git a/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php b/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php index 153fe1be5e9c..97dff1aa52bd 100755 --- a/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php @@ -154,17 +154,6 @@ protected function compileJsonContains($column, $value) return $value.' in (select [value] from openjson('.$field.$path.'))'; } - /** - * Compile a "JSON cast" statement into SQL. - * - * @param string $value - * @return string - */ - public function compileJsonCast($value) - { - return 'json_query('.$value.')'; - } - /** * Prepare the binding for a "JSON contains" statement. * @@ -216,6 +205,17 @@ protected function compileJsonLength($column, $operator, $value) return '(select count(*) from openjson('.$field.$path.')) '.$operator.' '.$value; } + /** + * Compile a "JSON value cast" statement into SQL. + * + * @param string $value + * @return string + */ + public function compileJsonValueCast($value) + { + return 'json_query('.$value.')'; + } + /** * Compile a single having clause. * diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php index 4235175f069f..7041d22606ca 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php @@ -181,7 +181,7 @@ public function castAsJson($value) $value = DB::connection()->getPdo()->quote($value); return DB::raw( - DB::connection()->getQueryGrammar()->compileJsonCast($value) + DB::connection()->getQueryGrammar()->compileJsonValueCast($value) ); }