Skip to content

Commit

Permalink
Add ExtractFunction for PostgreSQL (beberlei#331)
Browse files Browse the repository at this point in the history
  • Loading branch information
stevelacey authored Nov 22, 2019
2 parents 8003dfb + e0cd426 commit 5d5a725
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ MySQL, Oracle, PostgreSQL and SQLite.
| 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_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, 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, UNHEX, UNIX_TIMESTAMP, UTC_TIMESTAMP, UUID_SHORT, VARIANCE, WEEK, WEEKDAY, YEAR, YEARMONTH, YEARWEEK` |
| Oracle | `DAY, LISTAGG, MONTH, NVL, TO_CHAR, TO_DATE, TRUNC, YEAR` |
| Sqlite | `DATE, MINUTE, HOUR, DAY, WEEK, WEEKDAY, MONTH, YEAR, JULIANDAY, STRFTIME, DATE_FORMAT*, CASE WHEN THEN ELSE END, IFNULL, REPLACE, ROUND` |
| PostgreSQL | `DATE_PART, GREATEST, LEAST, COUNT_FILTER, REGEXP_REPLACE, STRING_AGG, TO_DATE, TO_CHAR, AT_TIME_ZONE` |
| PostgreSQL | `AT_TIME_ZONE, COUNT_FILTER, DATE_PART, EXTRACT, GREATEST, LEAST, REGEXP_REPLACE, STRING_AGG, TO_CHAR, TO_DATE` |

> Note: Sqlite date functions are implemented as `strftime(format, value)`.
Sqlite only supports the [most common formats](https://www.sqlite.org/lang_datefunc.html),
Expand Down
1 change: 1 addition & 0 deletions config/postgres.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ doctrine:
date_format: DoctrineExtensions\Query\Postgresql\DateFormat
at_time_zone: DoctrineExtensions\Query\Postgresql\AtTimeZoneFunction
date_part: DoctrineExtensions\Query\Postgresql\DatePart
extract: DoctrineExtensions\Query\Postgresql\ExtractFunction
string_functions:
str_to_date: DoctrineExtensions\Query\Postgresql\StrToDate
count_filter: DoctrineExtensions\Query\Postgresql\CountFilterFunction
Expand Down
55 changes: 55 additions & 0 deletions src/Query/Postgresql/ExtractFunction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace DoctrineExtensions\Query\Postgresql;

use Doctrine\ORM\Query\AST\ASTException;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\PathExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\SqlWalker;

class ExtractFunction extends FunctionNode
{
/** @var string */
private $field;

/** @var PathExpression */
private $value;

/**
* @param SqlWalker $sqlWalker
*
* @throws ASTException
* @return string
*/
public function getSql(SqlWalker $sqlWalker): string
{
return sprintf(
'EXTRACT(%s FROM %s)',
$this->field,
$this->value->dispatch($sqlWalker)
);
}

/**
* @param Parser $parser
*
* @throws QueryException
*/
public function parse(Parser $parser): void
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);

$parser->match(Lexer::T_IDENTIFIER);
$this->field = $parser->getLexer()->token['value'];

$parser->match(Lexer::T_FROM);

$this->value = $parser->ScalarExpression();

$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
21 changes: 21 additions & 0 deletions tests/Query/Postgresql/ExtractFunctionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace DoctrineExtensions\Tests\Query\Postgresql;

use Doctrine\ORM\QueryBuilder;
use DoctrineExtensions\Tests\Query\PostgresqlTestCase;

class ExtractFunctionTest extends PostgresqlTestCase
{
public function testExtract(): void
{
$queryBuilder = new QueryBuilder($this->entityManager);
$queryBuilder
->select('extract(EPOCH FROM dt.created)')
->from('DoctrineExtensions\Tests\Entities\Date', 'dt');

$expected = 'SELECT EXTRACT(EPOCH FROM d0_.created) AS sclr_0 FROM Date d0_';

$this->assertEquals($expected, $queryBuilder->getQuery()->getSQL());
}
}

0 comments on commit 5d5a725

Please sign in to comment.