-
Notifications
You must be signed in to change notification settings - Fork 11.3k
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
[9.x] Database queries containing JSON paths support array index references #38391
[9.x] Database queries containing JSON paths support array index references #38391
Conversation
0309f2c
to
c1d8a25
Compare
I guess there's no CI test suite feedback targeting the 9.x |
Thanks for sending this in @derekmd! Yes, I'm gonna try to get those sorted out today. |
Put this PR in draft for now. |
@derekmd you can rebase this now : ) |
e.g., DB::table('owner') ->where('packages[1]->name', 'laravel/framework') ->update(['packages[1]->versions[0]' => '9.0.0']); Stop compiling: UPDATE `owner` SET `packages` = JSON_SET(`packages`, $"[1]"."versions[0]", '9.0.0') WHERE json_unquote(json_extract(`packages[1]`, '$."name"')) = 'laravel/framework'; ... Instead avoid escaping array dereference characters: UPDATE `owner` SET `framework` = JSON_SET(`framework`, $[1]."versions"[0], '9.0.0') WHERE json_unquote(json_extract(`packages[1]`, '$."name"')) = 'laravel/framework';
c1d8a25
to
f7c5b9e
Compare
Thanks! |
Thanks @derekmd! 👏 |
Is there a limitation on including this in the next laravel 8 minor? Is it backwords breaking? Always running into issues writing tests around this issue. January 25th, 2022 for Laravel 9 seems so far off for this feature :( |
@CraigMonva: It's possible for apps to introduce the fix with 8.x by extending the app/Database/JsonPathFixGrammar.phpnamespace App\Database;
// extend the driver-appropriate grammar for your DB connection, like MySqlGrammar
use Illuminate\Database\Query\Grammars\MySqlGrammar;
use Illuminate\Support\Str;
class JsonPathFixGrammar extends MySqlGrammar
{
protected function wrapJsonPath($value, $delimiter = '->')
{
$value = preg_replace("/([\\\\]+)?\\'/", "''", $value);
$jsonPath = collect(explode($delimiter, $value))
->map(function ($segment) {
return $this->wrapJsonPathSegment($segment);
})
->join('.');
return "'$".(Str::startsWith($jsonPath, '[') ? '' : '.').$jsonPath."'";
}
protected function wrapJsonPathSegment($segment)
{
if (preg_match('/(\[[^\]]+\])+$/', $segment, $parts)) {
$key = Str::beforeLast($segment, $parts[0]);
if (! empty($key)) {
return '"'.$key.'"'.$parts[0];
}
return $parts[0];
}
return '"'.$segment.'"';
}
} app/Providers/AppServiceProvider.phpnamespace App\Providers;
use App\Database\JsonPathFixGrammar;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
DB::connection()->setQueryGrammar(new JsonPathFixGrammar);
}
} |
Hi Derek. Upgrading to L9 which should include this? Am I miss understanding what this change was meant to fix? My tests pass without asserting 'preferences->energy->channel->0' => 'email', |
Jesus christ nevermind. Thought Id been given a laravel shift branch with composer update already run on it. Aparently not. |
Fixes: #26415
When running queries that update JSON paths including an array index, Laravel compiles an SQL statement that escapes the array index as part of a JSON object key.
e.g.,
Database column
owner
.packages
row with JSON payload:For MySQL this compiles to:
Which would instead append to the above payload with a new nested JSON object key:
When escaping JSON path segments, avoid enclosing array dereference characters
[
and]
:Database documentation for JSON path support
Each of Laravel's supported database drivers use the same syntax for JSON path array indices.
JSON path features supported by database drivers
For the above features, Laravel supports the first three columns that all the database drivers implement. Segment-matching
*
/**
characters partially supported by some drivers still require Laravel developers to use raw SQL.9.x upgrade guide
In the low likelihood use case that applications use JSON column payload object key names ending in regex
\[.+\]
, databases will need to be updated to no longer use such characters.