Skip to content

Commit 9dd31e4

Browse files
authored
Add JSON_CONTAINS for MySQL (#354)
1 parent f19e560 commit 9dd31e4

File tree

4 files changed

+86
-1
lines changed

4 files changed

+86
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ MySQL, Oracle, PostgreSQL and SQLite.
1313

1414
| DB | Functions |
1515
|:--:|:---------:|
16-
| MySQL | `ACOS, ADDTIME, AES_DECRYPT, AES_ENCRYPT, ANY_VALUE, ASCII, ASIN, ATAN, ATAN2, BINARY, BIT_COUNT, BIT_XOR, CAST, CEIL, CHAR_LENGTH, COLLATE, CONCAT_WS, CONVERT_TZ, COS, COT, COUNTIF, CRC32, DATE, DATE_FORMAT, DATEADD, DATEDIFF, DATESUB, DAY, DAYNAME, DAYOFWEEK, DAYOFYEAR, DEGREES, DIV, EXP, EXTRACT, FIELD, FIND_IN_SET, FLOOR, FORMAT, FROM_BASE64, FROM_UNIXTIME, GREATEST, GROUP_CONCAT, HEX, HOUR, IFELSE, IFNULL, INET_ATON, INET_NTOA, INET6_ATON, INET6_NTOA, INSTR, IS_IPV4, IS_IPV4_COMPAT, IS_IPV4_MAPPED, IS_IPV6, JSON_DEPTH, LAG, LAST_DAY, LEAD, LEAST, LOG, LOG10, LOG2, LPAD, MAKEDATE, MATCH, MD5, MINUTE, MONTH, MONTHNAME, NOW, NULLIF, OVER, PERIOD_DIFF, PI, POWER, QUARTER, RADIANS, RAND, REGEXP, REPLACE, ROUND, RPAD, SECOND, SECTOTIME, SHA1, SHA2, SIN, SOUNDEX, STD, STDDEV, STRTODATE, STR_TO_DATE, SUBSTRING_INDEX, TAN, TIME, TIMEDIFF, TIMESTAMPADD, TIMESTAMPDIFF, TIMETOSEC, TRUNCATE, UNHEX, UNIX_TIMESTAMP, UTC_TIMESTAMP, UUID_SHORT, VARIANCE, WEEK, WEEKDAY, YEAR, YEARMONTH, YEARWEEK` |
16+
| MySQL | `ACOS, ADDTIME, AES_DECRYPT, AES_ENCRYPT, ANY_VALUE, ASCII, ASIN, ATAN, ATAN2, BINARY, BIT_COUNT, BIT_XOR, CAST, CEIL, CHAR_LENGTH, COLLATE, CONCAT_WS, CONVERT_TZ, COS, COT, COUNTIF, CRC32, DATE, DATE_FORMAT, DATEADD, DATEDIFF, DATESUB, DAY, DAYNAME, DAYOFWEEK, DAYOFYEAR, DEGREES, DIV, EXP, EXTRACT, FIELD, FIND_IN_SET, FLOOR, FORMAT, FROM_BASE64, FROM_UNIXTIME, GREATEST, GROUP_CONCAT, HEX, HOUR, IFELSE, IFNULL, INET_ATON, INET_NTOA, INET6_ATON, INET6_NTOA, INSTR, IS_IPV4, IS_IPV4_COMPAT, IS_IPV4_MAPPED, IS_IPV6, JSON_CONTAINS, JSON_DEPTH, LAG, LAST_DAY, LEAD, LEAST, LOG, LOG10, LOG2, LPAD, MAKEDATE, MATCH, MD5, MINUTE, MONTH, MONTHNAME, NOW, NULLIF, OVER, PERIOD_DIFF, PI, POWER, QUARTER, RADIANS, RAND, REGEXP, REPLACE, ROUND, RPAD, SECOND, SECTOTIME, SHA1, SHA2, SIN, SOUNDEX, STD, STDDEV, STRTODATE, STR_TO_DATE, SUBSTRING_INDEX, TAN, TIME, TIMEDIFF, TIMESTAMPADD, TIMESTAMPDIFF, TIMETOSEC, TRUNCATE, UNHEX, UNIX_TIMESTAMP, UTC_TIMESTAMP, UUID_SHORT, VARIANCE, WEEK, WEEKDAY, YEAR, YEARMONTH, YEARWEEK` |
1717
| Oracle | `CEIL, DAY, FLOOR, HOUR, LISTAGG, MINUTE, MONTH, NVL, SECOND, TO_CHAR, TO_DATE, TRUNC, YEAR` |
1818
| Sqlite | `CASE WHEN THEN ELSE END, DATE, DATE_FORMAT*, DAY, HOUR, IFNULL, JULIANDAY, MINUTE, MONTH, REPLACE, ROUND, SECOND, STRFTIME, WEEK, WEEKDAY, YEAR` |
1919
| PostgreSQL | `AT_TIME_ZONE, COUNT_FILTER, DATE, DATE_PART, DATE_TRUNC, DAY, EXTRACT, GREATEST, HOUR, LEAST, MINUTE, MONTH, REGEXP_REPLACE, SECOND, STRING_AGG, TO_CHAR, TO_DATE, YEAR` |

config/mysql.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ doctrine:
5454
degrees: DoctrineExtensions\Query\Mysql\Degrees
5555
exp: DoctrineExtensions\Query\Mysql\Exp
5656
floor: DoctrineExtensions\Query\Mysql\Floor
57+
json_contains: DoctrineExtensions\Query\Mysql\JsonContains
5758
json_depth: DoctrineExtensions\Query\Mysql\JsonDepth
5859
log: DoctrineExtensions\Query\Mysql\Log
5960
log10: DoctrineExtensions\Query\Mysql\Log10

src/Query/Mysql/JsonContains.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace DoctrineExtensions\Query\Mysql;
4+
5+
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
6+
use Doctrine\ORM\Query\Lexer;
7+
use Doctrine\ORM\Query\Parser;
8+
use Doctrine\ORM\Query\SqlWalker;
9+
10+
class JsonContains extends FunctionNode
11+
{
12+
protected $target;
13+
14+
protected $candidate;
15+
16+
protected $path;
17+
18+
public function parse(Parser $parser)
19+
{
20+
$parser->match(Lexer::T_IDENTIFIER);
21+
$parser->match(Lexer::T_OPEN_PARENTHESIS);
22+
23+
$this->target = $parser->StringPrimary();
24+
25+
$parser->match(Lexer::T_COMMA);
26+
27+
$this->candidate = $parser->StringPrimary();
28+
29+
if ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) {
30+
$parser->match(Lexer::T_COMMA);
31+
32+
$this->path = $parser->StringPrimary();
33+
}
34+
35+
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
36+
}
37+
38+
public function getSql(SqlWalker $sqlWalker)
39+
{
40+
$target = $sqlWalker->walkStringPrimary($this->target);
41+
$candidate = $sqlWalker->walkStringPrimary($this->candidate);
42+
43+
if ($this->path !== null) {
44+
$path = $sqlWalker->walkStringPrimary($this->path);
45+
46+
return sprintf('JSON_CONTAINS(%s, %s, %s)', $target, $candidate, $path);
47+
}
48+
49+
return sprintf('JSON_CONTAINS(%s, %s)', $target, $candidate);
50+
}
51+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace DoctrineExtensions\Tests\Query\Mysql;
4+
5+
use DoctrineExtensions\Tests\Query\MysqlTestCase;
6+
7+
class JsonContainsTest extends MysqlTestCase
8+
{
9+
public function testJsonContainsWithoutPath(): void
10+
{
11+
$this->assertDqlProducesSql(
12+
"SELECT JSON_CONTAINS('{}', :param) from DoctrineExtensions\Tests\Entities\Blank as blank",
13+
"SELECT JSON_CONTAINS('{}', ?) AS sclr_0 FROM Blank b0_",
14+
['param' => '']
15+
);
16+
}
17+
18+
public function testJsonContainsWithPathAsParam(): void
19+
{
20+
$this->assertDqlProducesSql(
21+
"SELECT JSON_CONTAINS('{}', :param, :path) from DoctrineExtensions\Tests\Entities\Blank as blank",
22+
"SELECT JSON_CONTAINS('{}', ?, ?) AS sclr_0 FROM Blank b0_"
23+
);
24+
}
25+
26+
public function testJsonContainsWithPathAsExplicit(): void
27+
{
28+
$this->assertDqlProducesSql(
29+
"SELECT JSON_CONTAINS('{}', :param, '$[0]') from DoctrineExtensions\Tests\Entities\Blank as blank",
30+
"SELECT JSON_CONTAINS('{}', ?, '$[0]') AS sclr_0 FROM Blank b0_"
31+
);
32+
}
33+
}

0 commit comments

Comments
 (0)