-
Notifications
You must be signed in to change notification settings - Fork 11.7k
Description
Laravel Version
10.44
PHP Version
8.1
Database Driver & Version
SQLite 3.42
Description
I have existing queries with whereJsonContains and some tests using it started failing after I migrated the framework from 9.x to 10.44. Up until now we had a patch that uses PDO sqliteCreateFunction in order to fake the exact same behaviour of the MySQL json_contains into SQLite.
However I saw native support in Laravel 10 added with this PR and tried to use it instead of our internal one. The query is built like bellow:
$query->whereJsonContains('value', ['enabled' => true, 'some_other_flag' => false]);
In MySQL grammar the above compiles to
select * from `settings` where json_contains(`value`, '{\"enabled\":true,\"some_other_flag\":false}');
In SQLite while running tests the same query compiles to
select * from "settings" where exists (select 1 from json_each("value") where "json_each"."value" is 1);
The above has couple of issues
- it does not take into consideration
enabledandsome_other_flagjson properties and their values - it will always return 0 rows because of
json_each("value"), however if you prefix it with the table namejson_each("settings"."value")it works, but of course returns all rows containing any kind of json value that is treated as "truthy"
What I'm expecting to get is something like
select * from "settings" where exists (select 1 from json_each(settings.value, '$."enabled"') where "json_each"."value" is 1) and exists (select 1 from json_each(settings.value, '$."some_other_flag"') where "json_each"."value" is 0)
That would have exactly the same behaviour like in MySQL.
If I change
$query->whereJsonContains('value', ['enabled' => true, 'some_other_flag' => false]);
to
$query->whereJsonContains('value->enabled', true)->whereJsonContains('value-> some_other_flag', false);
I can bypass issue 1, but still I can't get around issue 2. I thought it's because "value" is double quoted, but I tried with json_each('value') and json_each(value) without luck, so its something with the internals of the json_each function and the value column name.
Steps To Reproduce
- Create model and table with json column named
value - Insert 3 records with the following values
{"enabled":true,"some_other_flag":true},{"enabled":false,"some_other_flag":false},{"enabled":true,"some_other_flag":false} - With a query builder try to fetch
$query->whereJsonContains('value', ['enabled' => true, 'some_other_flag' => false]); - Write unit test that checks the result
In MySQL this will return 1 record, In SQLite they will be 3.