Skip to content

[9.x] Database queries containing JSON paths support array index braces, part 2 #41767

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged

Conversation

derekmd
Copy link
Contributor

@derekmd derekmd commented Mar 31, 2022

Follow-up to #38391 meant to fix #26415
Related to today's PR #41756

  1. DB::table('json_table')
        ->where('column->json_option[0]', 'foo')
        ->update(['column->json_option[0]', => 'bar']);

    The previous PR allowed the above query to match by JSON array index. But the fixes didn't apply to the Postgres database driver that overrides PostgresGrammer@wrapJsonSelector().

    Postgres does already support this:

    DB::table('json_table')
       ->where('column->json_option->0', 'foo')
       ->update(['column->json_option->0', => 'bar']);

    This fix allows the first PHP snippet to also work, bringing it in line with the other database drivers. SQL:

    -- before
    update "json_table"
    set "column" = jsonb_set("column"::jsonb, '{"json_option[0]"}', '"bar"'::jsonb)
    where ("column"->'json_option[0]')::jsonb = '"foo"'::jsonb;
    
    -- after, array index is extracted and braces are stripped
    update "json_table"
    set "column" = jsonb_set("column"::jsonb, '{"json_option",0}', '"bar"'::jsonb)
    where ("column"->'json_option'->0)::jsonb = '"foo"'::jsonb;

    Now the GitHub Actions config has a Postgres container I've added some integration tests.

  2. Illuminate\Database\Schema\Grammars\Grammar@wrapJsonFieldAndPath() was a copy and paste duplicate from Illuminate\Database\Query\Grammars\Grammar and didn't receive the same fixes from the previous PR. It's a far edge case for column methods virtualAsJson() and storedAsJson() to use an array index but it probably makes sense for these classes to share a trait for compiling SQL containing JSON paths.

Illuminate\Database\Query\Grammars\Grammar@wrapJsonFieldAndPath() and
Illuminate\Database\Schema\Grammars\Grammar@wrapJsonFieldAndPath() were
copy and pasted duplicates but the query version had JSON path fixes
applied that the schema version doesn't have.

Instead add a trait to make the two classes share these methods.
*
* @param array $path
* @return array
*/
protected function wrapJsonPathAttributes($path)
{
return array_map(function ($attribute) {
$quote = func_num_args() === 2 ? func_get_arg(1) : "'";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    protected function wrapJsonPathAttributes($path, $quote = "'")
    {

I forget if Laravel considers protected method argument changes as breaking. I guess userland may extend PostgresGrammar and override this method.

Similar to MySQL, SQL Server, and SQLite, make Postgres support
`$query->where('column->json_key[0]', 'foo')`. Postgres also allows
equivalent call `$query->where('column->json_key->0', 'foo')`.

Unlike the other database drivers, the SQL doesn't compile to a JSON
path expression. The array indices must be parsed from the string,
separating them into new segments. e.g.,

$query->where('column->json_key[0]', 'foo')
@derekmd derekmd force-pushed the fix-postgres-json-paths-with-array-index branch from fb5179c to ab59bf5 Compare April 1, 2022 14:39
@taylorotwell taylorotwell merged commit d80471d into laravel:9.x Apr 1, 2022
@derekmd derekmd deleted the fix-postgres-json-paths-with-array-index branch April 1, 2022 15:11
@taylorotwell
Copy link
Member

Thanks @derekmd! We would be pretty lost without you 😅

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

mysql json field cannot update nested array item through nested array's index
2 participants