Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
namespace MartinGeorgiev\Doctrine\ORM\Query\AST\Functions;

/**
* Implementation of PostgreSQL REGEXP_LIKE() with flags.
* @deprecated This function will be dropped in v4.0. Use RegexpLike instead.
*
* Implementation of PostgreSQL REGEXP_LIKE() with flags.
* @see https://www.postgresql.org/docs/15/functions-matching.html#FUNCTIONS-POSIX-REGEXP
* @see https://www.postgresql.org/docs/15/functions-matching.html#POSIX-EMBEDDED-OPTIONS-TABLE
* @since 2.0
Expand All @@ -17,7 +18,7 @@ class FlaggedRegexpLike extends BaseFunction
{
protected function customizeFunction(): void
{
$this->setFunctionPrototype('regexp_like(%s, %s, 1, %s)');
Copy link
Owner Author

Choose a reason for hiding this comment

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

This was actually a bug that never worked as intended.

$this->setFunctionPrototype('regexp_like(%s, %s, %s)');
$this->addNodeMapping('StringPrimary');
$this->addNodeMapping('StringPrimary');
$this->addNodeMapping('StringPrimary');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
namespace MartinGeorgiev\Doctrine\ORM\Query\AST\Functions;

/**
* Implementation of PostgreSQL REGEXP_MATCH() with flags.
* @deprecated This function will be dropped in v4.0. Use RegexpMatch instead.
*
* Implementation of PostgreSQL REGEXP_MATCH() with flags.
* @see https://www.postgresql.org/docs/15/functions-matching.html#FUNCTIONS-POSIX-REGEXP
* @see https://www.postgresql.org/docs/15/functions-matching.html#POSIX-EMBEDDED-OPTIONS-TABLE
* @since 2.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
namespace MartinGeorgiev\Doctrine\ORM\Query\AST\Functions;

/**
* Implementation of PostgreSQL REGEXP_REPLACE().
* @deprecated This function will be dropped in v4.0. Use RegexpReplace instead.
*
* Implementation of PostgreSQL REGEXP_REPLACE().
* @see https://www.postgresql.org/docs/15/functions-matching.html#FUNCTIONS-POSIX-REGEXP
* @since 2.5
*
Expand Down
22 changes: 19 additions & 3 deletions src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RegexpLike.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,36 @@
/**
* Implementation of PostgreSQL REGEXP_LIKE().
*
* @see https://www.postgresql.org/docs/15/functions-matching.html#FUNCTIONS-POSIX-REGEXP
* Returns true if a string matches a POSIX regular expression pattern, or false if it does not.
*
* @see https://www.postgresql.org/docs/17/functions-matching.html#FUNCTIONS-POSIX-REGEXP
* @since 2.0
*
* @author Martin Georgiev <martin.georgiev@gmail.com>
*
* @example Using it in DQL: "SELECT REGEXP_LIKE(e.text, 'pattern', 'i') FROM Entity e"
*/
class RegexpLike extends BaseRegexpFunction
class RegexpLike extends BaseVariadicFunction
{
protected function getNodeMappingPattern(): array
{
return [
'StringPrimary',
];
}

protected function getFunctionName(): string
{
return 'regexp_like';
}

protected function getParameterCount(): int
protected function getMinArgumentCount(): int
{
return 2;
}

protected function getMaxArgumentCount(): int
{
return 3;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,36 @@
/**
* Implementation of PostgreSQL REGEXP_MATCH().
*
* @see https://www.postgresql.org/docs/15/functions-matching.html#FUNCTIONS-POSIX-REGEXP
* Returns the first substring(s) that match a POSIX regular expression pattern, or NULL if there is no match.
*
* @see https://www.postgresql.org/docs/17/functions-matching.html#FUNCTIONS-POSIX-REGEXP
* @since 2.0
*
* @author Martin Georgiev <martin.georgiev@gmail.com>
*
* @example Using it in DQL: "SELECT REGEXP_MATCH(e.text, 'pattern', 'i') FROM Entity e"
*/
class RegexpMatch extends BaseRegexpFunction
class RegexpMatch extends BaseVariadicFunction
{
protected function getNodeMappingPattern(): array
{
return [
'StringPrimary',
];
}

protected function getFunctionName(): string
{
return 'regexp_match';
}

protected function getParameterCount(): int
protected function getMinArgumentCount(): int
{
return 2;
}

protected function getMaxArgumentCount(): int
{
return 3;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,25 @@
* @since 2.5
*
* @author Colin Doig
* @author Martin Georgiev <martin.georgiev@gmail.com>
*
* @example Using it in DQL: "SELECT REGEXP_REPLACE(e.text, 'pattern', 'replacement', 3, 2, 'i') FROM Entity e"
*/
class RegexpReplace extends BaseVariadicFunction
{
protected function getNodeMappingPattern(): array
{
/*
* PostgreSQL overloads the 4th argument depending on its type:
* - if the 4th arg is a string, it’s taken as flags.
* - if the 4th arg is an integer, it’s taken as start position. This can be extended with the Nth argument.
*/
return [
'StringPrimary,StringPrimary,StringPrimary,ArithmeticPrimary,ArithmeticPrimary,StringPrimary',
'StringPrimary,StringPrimary,StringPrimary,ArithmeticPrimary,ArithmeticPrimary',
'StringPrimary,StringPrimary,StringPrimary,ArithmeticPrimary,StringPrimary',
'StringPrimary,StringPrimary,StringPrimary,ArithmeticPrimary',
'StringPrimary,StringPrimary,StringPrimary,StringPrimary',
'StringPrimary,StringPrimary,StringPrimary',
'StringPrimary,StringPrimary,StringPrimary,ArithmeticPrimary,ArithmeticPrimary,StringPrimary', // with start, N and flags: regexp_replace(string, pattern, replacement, 3, 2, 'i')
'StringPrimary,StringPrimary,StringPrimary,ArithmeticPrimary,ArithmeticPrimary', // with start and N: regexp_replace(string, pattern, replacement, 3, 2)
'StringPrimary,StringPrimary,StringPrimary,ArithmeticPrimary', // with start: regexp_replace(string, pattern, replacement, 3)
'StringPrimary,StringPrimary,StringPrimary,StringPrimary', // with flags: regexp_replace(string, pattern, replacement, 'i')
'StringPrimary,StringPrimary,StringPrimary', // basic replacement: regexp_replace(string, pattern, replacement)
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function throws_an_exception_when_lexer_is_not_populated_with_a_lookahead
->willReturn(new Configuration());

$query = new Query($em);
$query->setDQL('TRUE');
$query->setDQL('SELECT 1');

$parser = new Parser($query);
$parser->getLexer()->moveNext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ class FlaggedRegexpLikeTest extends TestCase
protected function getStringFunctions(): array
{
return [
'FLAGGED_REGEXP_LIKE' => FlaggedRegexpLike::class,
'FLAGGED_REGEXP_LIKE' => FlaggedRegexpLike::class, // @phpstan-ignore-line
];
}

protected function getExpectedSqlStatements(): array
{
return [
"SELECT regexp_like(c0_.text1, 'pattern', 1, 'i') AS sclr_0 FROM ContainsTexts c0_",
"SELECT regexp_like(c0_.text1, 'pattern', 'i') AS sclr_0 FROM ContainsTexts c0_",
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class FlaggedRegexpMatchTest extends TestCase
protected function getStringFunctions(): array
{
return [
'FLAGGED_REGEXP_MATCH' => FlaggedRegexpMatch::class,
'FLAGGED_REGEXP_MATCH' => FlaggedRegexpMatch::class, // @phpstan-ignore-line
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class FlaggedRegexpReplaceTest extends TestCase
protected function getStringFunctions(): array
{
return [
'FLAGGED_REGEXP_REPLACE' => FlaggedRegexpReplace::class,
'FLAGGED_REGEXP_REPLACE' => FlaggedRegexpReplace::class, // @phpstan-ignore-line
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@
namespace Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions;

use Fixtures\MartinGeorgiev\Doctrine\Entity\ContainsTexts;
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\BaseVariadicFunction;
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Exception\InvalidArgumentForVariadicFunctionException;
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\RegexpLike;

class RegexpLikeTest extends TestCase
class RegexpLikeTest extends BaseVariadicFunctionTestCase
{
protected function createFixture(): BaseVariadicFunction
{
return new RegexpLike('REGEXP_LIKE');
}

protected function getStringFunctions(): array
{
return [
Expand All @@ -19,14 +26,34 @@ protected function getStringFunctions(): array
protected function getExpectedSqlStatements(): array
{
return [
"SELECT regexp_like(c0_.text1, 'pattern') AS sclr_0 FROM ContainsTexts c0_",
'basic match' => "SELECT regexp_like(c0_.text1, 'pattern') AS sclr_0 FROM ContainsTexts c0_",
'with flags' => "SELECT regexp_like(c0_.text1, 'pattern', 'i') AS sclr_0 FROM ContainsTexts c0_",
];
}

protected function getDqlStatements(): array
{
return [
\sprintf("SELECT REGEXP_LIKE(e.text1, 'pattern') FROM %s e", ContainsTexts::class),
'basic match' => \sprintf("SELECT REGEXP_LIKE(e.text1, 'pattern') FROM %s e", ContainsTexts::class),
'with flags' => \sprintf("SELECT REGEXP_LIKE(e.text1, 'pattern', 'i') FROM %s e", ContainsTexts::class),
];
}

public function test_too_few_arguments_throws_exception(): void
{
$this->expectException(InvalidArgumentForVariadicFunctionException::class);
$this->expectExceptionMessage('regexp_like() requires at least 2 arguments');

$dql = \sprintf('SELECT REGEXP_LIKE(e.text1) FROM %s e', ContainsTexts::class);
$this->buildEntityManager()->createQuery($dql)->getSQL();
}

public function test_too_many_arguments_throws_exception(): void
{
$this->expectException(InvalidArgumentForVariadicFunctionException::class);
$this->expectExceptionMessage('regexp_like() requires between 2 and 3 arguments');

$dql = \sprintf("SELECT REGEXP_LIKE(e.text1, 'pattern', 'i', 'extra_arg') FROM %s e", ContainsTexts::class);
$this->buildEntityManager()->createQuery($dql)->getSQL();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@
namespace Tests\MartinGeorgiev\Doctrine\ORM\Query\AST\Functions;

use Fixtures\MartinGeorgiev\Doctrine\Entity\ContainsTexts;
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\BaseVariadicFunction;
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Exception\InvalidArgumentForVariadicFunctionException;
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\RegexpMatch;

class RegexpMatchTest extends TestCase
class RegexpMatchTest extends BaseVariadicFunctionTestCase
{
protected function createFixture(): BaseVariadicFunction
{
return new RegexpMatch('REGEXP_MATCH');
}

protected function getStringFunctions(): array
{
return [
Expand All @@ -19,14 +26,34 @@ protected function getStringFunctions(): array
protected function getExpectedSqlStatements(): array
{
return [
"SELECT regexp_match(c0_.text1, 'pattern') AS sclr_0 FROM ContainsTexts c0_",
'basic match' => "SELECT regexp_match(c0_.text1, 'pattern') AS sclr_0 FROM ContainsTexts c0_",
'with flags' => "SELECT regexp_match(c0_.text1, 'pattern', 'i') AS sclr_0 FROM ContainsTexts c0_",
];
}

protected function getDqlStatements(): array
{
return [
\sprintf("SELECT REGEXP_MATCH(e.text1, 'pattern') FROM %s e", ContainsTexts::class),
'basic match' => \sprintf("SELECT REGEXP_MATCH(e.text1, 'pattern') FROM %s e", ContainsTexts::class),
'with flags' => \sprintf("SELECT REGEXP_MATCH(e.text1, 'pattern', 'i') FROM %s e", ContainsTexts::class),
];
}

public function test_too_few_arguments_throws_exception(): void
{
$this->expectException(InvalidArgumentForVariadicFunctionException::class);
$this->expectExceptionMessage('regexp_match() requires at least 2 arguments');

$dql = \sprintf('SELECT REGEXP_MATCH(e.text1) FROM %s e', ContainsTexts::class);
$this->buildEntityManager()->createQuery($dql)->getSQL();
}

public function test_too_many_arguments_throws_exception(): void
{
$this->expectException(InvalidArgumentForVariadicFunctionException::class);
$this->expectExceptionMessage('regexp_match() requires between 2 and 3 arguments');

$dql = \sprintf("SELECT REGEXP_MATCH(e.text1, 'pattern', 'i', 'extra_arg') FROM %s e", ContainsTexts::class);
$this->buildEntityManager()->createQuery($dql)->getSQL();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ protected function getExpectedSqlStatements(): array
'basic replacement' => "SELECT regexp_replace(c0_.text1, 'pattern', 'replacement') AS sclr_0 FROM ContainsTexts c0_",
'with flags but no start position' => "SELECT regexp_replace(c0_.text1, 'pattern', 'replacement', 'i') AS sclr_0 FROM ContainsTexts c0_",
'with start position' => "SELECT regexp_replace(c0_.text1, 'pattern', 'replacement', 3) AS sclr_0 FROM ContainsTexts c0_",
'with start position and flags' => "SELECT regexp_replace(c0_.text1, 'pattern', 'replacement', 3, 'i') AS sclr_0 FROM ContainsTexts c0_",
'with occurrence count but no flags' => "SELECT regexp_replace(c0_.text1, 'pattern', 'replacement', 3, 2) AS sclr_0 FROM ContainsTexts c0_",
'with occurrence count and flags' => "SELECT regexp_replace(c0_.text1, 'pattern', 'replacement', 3, 2, 'i') AS sclr_0 FROM ContainsTexts c0_",
];
Expand All @@ -41,7 +40,6 @@ protected function getDqlStatements(): array
'basic replacement' => \sprintf("SELECT REGEXP_REPLACE(e.text1, 'pattern', 'replacement') FROM %s e", ContainsTexts::class),
'with flags but no start position' => \sprintf("SELECT REGEXP_REPLACE(e.text1, 'pattern', 'replacement', 'i') FROM %s e", ContainsTexts::class),
'with start position' => \sprintf("SELECT REGEXP_REPLACE(e.text1, 'pattern', 'replacement', 3) FROM %s e", ContainsTexts::class),
'with start position and flags' => \sprintf("SELECT REGEXP_REPLACE(e.text1, 'pattern', 'replacement', 3, 'i') FROM %s e", ContainsTexts::class),
'with occurrence count but no flags' => \sprintf("SELECT REGEXP_REPLACE(e.text1, 'pattern', 'replacement', 3, 2) FROM %s e", ContainsTexts::class),
'with occurrence count and flags' => \sprintf("SELECT REGEXP_REPLACE(e.text1, 'pattern', 'replacement', 3, 2, 'i') FROM %s e", ContainsTexts::class),
];
Expand Down
Loading