diff --git a/README.md b/README.md index cc99c42d..8e422677 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ MySQL, Oracle, PostgreSQL and SQLite. | DB | Functions | |:--:|:---------:| -| 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, JSON_LENGTH, 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, WEEKOFYEAR, YEAR, YEARMONTH, YEARWEEK` | +| 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, JSON_LENGTH, JSON_EXTRACT, 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, WEEKOFYEAR, YEAR, YEARMONTH, YEARWEEK` | | Oracle | `CEIL, DAY, FLOOR, HOUR, LISTAGG, MINUTE, MONTH, NVL, SECOND, TO_CHAR, TO_DATE, TRUNC, YEAR` | | SQLite | `CASE WHEN THEN ELSE END, DATE, DATE_FORMAT*, DAY, HOUR, IFNULL, JULIANDAY, MINUTE, MONTH, REPLACE, ROUND, SECOND, STRFTIME, WEEK, WEEKDAY, YEAR` | | 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` | diff --git a/config/mysql.yml b/config/mysql.yml index a05c74e1..a79f119d 100644 --- a/config/mysql.yml +++ b/config/mysql.yml @@ -104,6 +104,7 @@ doctrine: is_ipv4_compat: DoctrineExtensions\Query\Mysql\IsIpv4Compat is_ipv4_mapped: DoctrineExtensions\Query\Mysql\IsIpv4Mapped is_ipv6: DoctrineExtensions\Query\Mysql\IsIpv6 + json_extract: DoctrineExtensions\Query\Mysql\JsonExtract lag: DoctrineExtensions\Query\Mysql\Lag lead: DoctrineExtensions\Query\Mysql\Lead least: DoctrineExtensions\Query\Mysql\Least diff --git a/src/Query/Mysql/JsonExtract.php b/src/Query/Mysql/JsonExtract.php new file mode 100644 index 00000000..a700c58b --- /dev/null +++ b/src/Query/Mysql/JsonExtract.php @@ -0,0 +1,43 @@ +match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->target = $parser->StringExpression(); + + $parser->match(Lexer::T_COMMA); + + $this->paths[] = $parser->StringPrimary(); + + while ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) { + $parser->match(Lexer::T_COMMA); + + $this->paths[] = $parser->StringPrimary(); + } + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } + + public function getSql(SqlWalker $sqlWalker) + { + $target = $sqlWalker->walkStringPrimary($this->target); + $paths = array_map([$sqlWalker, 'walkStringPrimary'], $this->paths); + + return sprintf('JSON_EXTRACT(%s, %s)', $target, implode(', ', $paths)); + } +} diff --git a/tests/Query/Mysql/JsonExtractTest.php b/tests/Query/Mysql/JsonExtractTest.php new file mode 100644 index 00000000..fddabd1d --- /dev/null +++ b/tests/Query/Mysql/JsonExtractTest.php @@ -0,0 +1,33 @@ +assertDqlProducesSql( + "SELECT JSON_EXTRACT('{}', :param) from DoctrineExtensions\Tests\Entities\Blank as blank", + "SELECT JSON_EXTRACT('{}', ?) AS sclr_0 FROM Blank b0_", + ['param' => ''] + ); + } + + public function testJsonContainsWithMultiplePaths(): void + { + $this->assertDqlProducesSql( + "SELECT JSON_EXTRACT('{}', :path1, :path2) from DoctrineExtensions\Tests\Entities\Blank as blank", + "SELECT JSON_EXTRACT('{}', ?, ?) AS sclr_0 FROM Blank b0_" + ); + } + + public function testJsonContainsWithPathAsExplicit(): void + { + $this->assertDqlProducesSql( + "SELECT JSON_EXTRACT('{}', '$[0]') from DoctrineExtensions\Tests\Entities\Blank as blank", + "SELECT JSON_EXTRACT('{}', '$[0]') AS sclr_0 FROM Blank b0_" + ); + } +}