Add JsonPathExpression to support json path clauses by othercorey · Pull Request #19463 · cakephp/cakephp · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
30 changes: 30 additions & 0 deletions src/Database/Driver/Mysql.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
use Cake\Database\Driver;
use Cake\Database\DriverFeatureEnum;
use Cake\Database\Expression\DistinctComparisonExpression;
use Cake\Database\Expression\FunctionExpression;
use Cake\Database\Expression\QueryExpression;
use Cake\Database\Expression\StringAggExpression;
use Cake\Database\Query;
use Cake\Database\Query\SelectQuery;
Expand All @@ -39,6 +41,7 @@ class Mysql extends Driver
protected function _expressionTranslators(): array
{
return [
FunctionExpression::class => '_transformFunctionExpression',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
FunctionExpression::class => '_transformFunctionExpression',
FunctionExpression::class => 'transformFunctionExpression',

StringAggExpression::class => 'transformStringAggExpression',
DistinctComparisonExpression::class => 'transformDistinctComparisonExpression',
];
Expand Down Expand Up @@ -355,6 +358,33 @@ public function version(): string
return $this->_version;
}

/**
* Receives a FunctionExpression and changes it so that it conforms to this SQL dialect.
*
* @param \Cake\Database\Expression\FunctionExpression $expression The function expression to transform.
* @return void
*/
protected function _transformFunctionExpression(FunctionExpression $expression): void

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Unless this is an overridden function we don't need the underscore prefix.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
protected function _transformFunctionExpression(FunctionExpression $expression): void
protected function transformFunctionExpression(FunctionExpression $expression): void

{
if ($this->isMariadb()) {
return;
}

switch ($expression->getName()) {
case 'JSON_EXISTS':
$expression
->setName('JSON_CONTAINS_PATH')
->iterateParts(function ($p, $key) {
if ($key === 1) {
return new QueryExpression(["'all'", $p], ['literal'], ',', parentheses: false);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we have a separate class for a ExpressionList style container that doesn't emit parentheses? I could see this being useful in other parts of the database drivers as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It would be helpful. Hoping one of you makes the call.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@LordSimal @ADmad Do you have any opinions on this so I can work on final update?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I have no strong opinion about this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@othercorey Adding a new class as markstory suggested would be cleaner instead of adding more switches to QueryExpression.

}

return $p;
});
break;
}
}

/**
* Get PDO ATTR_SSL_KEY id.
*
Expand Down
32 changes: 22 additions & 10 deletions src/Database/Driver/Postgres.php
Original file line number Diff line number Diff line change
Expand Up @@ -345,16 +345,28 @@ protected function _transformFunctionExpression(FunctionExpression $expression):
->add([') + (1' => 'literal']); // Postgres starts on index 0 but Sunday should be 1
break;
case 'JSON_VALUE':
$expression->setName('JSONB_PATH_QUERY')
->iterateParts(function ($p, $key) {
if ($key === 0) {
$p = sprintf('%s::jsonb', $p);
} elseif ($key === 1) {
$p = sprintf("'%s'::jsonpath", $this->quoteIdentifier($p['value']));
}

return $p;
});
if (version_compare($this->version(), '17.0', '<')) {
$expression->setName('JSONB_PATH_QUERY')
->iterateParts(function ($p, $key) {
if ($key === 0) {
return $p = sprintf('%s::jsonb', $p);
}

return $p;
});
}
break;
case 'JSON_EXISTS':
if (version_compare($this->version(), '17.0', '<')) {
$expression->setName('JSONB_PATH_EXISTS')
->iterateParts(function ($p, $key) {
if ($key === 0) {
return sprintf('%s::jsonb', $p);
}

return $p;
});
}
break;
}
}
Expand Down
17 changes: 17 additions & 0 deletions src/Database/Driver/Sqlite.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Cake\Database\Driver;
use Cake\Database\DriverFeatureEnum;
use Cake\Database\Expression\FunctionExpression;
use Cake\Database\Expression\QueryExpression;
use Cake\Database\Expression\StringAggExpression;
use Cake\Database\Expression\TupleComparison;
use Cake\Database\Schema\SchemaDialect;
Expand Down Expand Up @@ -325,6 +326,22 @@ protected function _transformFunctionExpression(FunctionExpression $expression):
case 'JSON_VALUE':
$expression->setName('JSON_EXTRACT');
break;
case 'JSON_EXISTS':
$expression
->setName('JSON_TYPE')
->iterateParts(function ($p, $key) {
if ($key === 1) {
return new QueryExpression(
[$p, ') IS NOT NULL AND (1'],
[null, 'literal'],
'',
parentheses: false,
);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

cake makes modifying function expressions into something else a nightmare. I'm not sure if this is worth it or not, but it's one way to support it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think it is worth the effort. SQLite is a fantastic database when it is a suitable choice.

}

return $p;
});
break;
}
}
}
242 changes: 242 additions & 0 deletions src/Database/Expression/JsonPathExpression.php
Loading
Loading