-
Notifications
You must be signed in to change notification settings - Fork 11k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SQLite whereJsonContains
grammar not working correctly
#50246
Comments
Thank you for reporting this issue! As Laravel is an open source project, we rely on the community to help us diagnose and fix issues as it is not possible to research and fix every issue reported to us via GitHub. If possible, please make a pull request fixing the issue you have described, along with corresponding tests. All pull requests are promptly reviewed by the Laravel team. Thank you! |
@danieleambrosino do you might know more about this one? |
That would be helpful, I think it's underlying issue with SQLite itself and opened a thread in their forum. |
Hi @dmtar, thank you very much for reporting the issue. For what concerns your first issue (passing arrays as the second argument of
This is because MySQL has a dedicated json_contains function, and PostgreSQL has a dedicated operator to check for containment and it even has a dedicated section in the documentation in which JSON containment is defined with examples. Moreover (I really don't want to look like the "RTFM guy", sorry for that!), the documentation states that:
and not objects or subdocuments. As I interpret it, an appropriate use of With that beings said, If I understood correctly your purpose, a possible approach to improve the cross-compatibility of your query is writing it like that: $query->where('value->enabled', true)->where('value->some_other_flag', false) Basic Nonetheless, I think you have spotted an interesting misbehaviour, which occurs in the edge case when the column of the queried table is called "value", that is the same name of the column of the I created a SQLite table populating it with the examples that you provided and I can confirm that the following query select * from "test" where exists (select 1 from json_each("value") where "json_each"."value" is true) wrongly does not return any rows, while select * from "test" where exists (select 1 from json_each("test"."value") where "json_each"."value" is true) returns the two rows containing a "truthy" value as one would expect. This should be easily fixed in the SQLite grammar by always passing the fully qualified column name (prefixing it with the table name) as the argument of |
Thank you very much for that thorough response @danieleambrosino. We'd appreciate a PR to remedy the issue of the non-qualified column! (if that's possible at all) |
@driesvints I was just taking a look at the SQLiteGrammar code, and in fact if you think about it it's not at all obvious to impose a fixed prefix for the column name (how should the query builder know a priori which table we are imposing the constraint on?) A possible way to handle this problem could be to somehow point out this quirk in the documentation or somewhere else, and suggest specifying the table name when imposing a constraint on a column named "value" on a SQLite DB (even if this is a very specific corner case and I don't know if it's worth reporting it in the documentation). In fact, the previously mentioned query can be created without problems with DB::table('test')->whereJsonContains('test.value', true) |
Ah oh, it's only with "value"? In that case I don't think any immediate action is needed. It's very unlikely lots of people will stumble upon this one. Gonna close this one then, I appreciate the help here from you both. |
@danieleambrosino Thank you very much for the detailed response. Completely agree with you that arrow syntax is the correct way. Since I'm upgrading a huge Laravel project from 9.x to 10 and these queries are way more complex than the example, I'm was more like "get all tests passing without code changes". |
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 MySQLjson_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:
In MySQL grammar the above compiles to
In SQLite while running tests the same query compiles to
The above has couple of issues
enabled
andsome_other_flag
json properties and their valuesjson_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
That would have exactly the same behaviour like in MySQL.
If I change
$query->whereJsonContains('value', ['enabled' => true, 'some_other_flag' => false]);
to
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')
andjson_each(value)
without luck, so its something with the internals of thejson_each
function and thevalue
column name.Steps To Reproduce
value
{"enabled":true,"some_other_flag":true}
,{"enabled":false,"some_other_flag":false}
,{"enabled":true,"some_other_flag":false}
$query->whereJsonContains('value', ['enabled' => true, 'some_other_flag' => false]);
In MySQL this will return 1 record, In SQLite they will be 3.
The text was updated successfully, but these errors were encountered: