Skip to content

Commit 91ede91

Browse files
Ocramiusmorozov
authored andcommitted
Merge pull request #3494 from morozov/get-locate-expr
Modified AbstractPlatform::getLocateExpression() and ::getSubstringExpression() signatures
2 parents 758a6c7 + cf82110 commit 91ede91

File tree

12 files changed

+111
-81
lines changed

12 files changed

+111
-81
lines changed

UPGRADE.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Upgrade to 3.0
22

3+
## BC BREAK The type of `$start` in `AbstractPlatform::getLocateExpression()` changed from `string|false` to `?string`
4+
5+
The default value of `$start` is now `null`, not `false`.
6+
7+
## BC BREAK The types of `$start` and `$length` in `AbstractPlatform::getSubstringExpression()` changed from `int` and `?int` to `string` and `?string` respectively
8+
9+
The platform abstraction allows building arbitrary SQL expressions, so even if the arguments represent numeric literals, they should be passed as a string.
10+
311
## BC BREAK The type of `$char` in `AbstractPlatform::getTrimExpression()` changed from `string|false` to `?string`
412

513
The default value of `$char` is now `null`, not `false`. Additionally, the method will throw an `InvalidArgumentException` in an invalid value of `$mode` is passed.

lib/Doctrine/DBAL/Platforms/AbstractPlatform.php

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -850,17 +850,16 @@ public function getLowerExpression($str)
850850
}
851851

852852
/**
853-
* Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str.
853+
* Returns the SQL snippet to get the position of the first occurrence of the substring in the string.
854854
*
855-
* @param string $str Literal string.
856-
* @param string $substr Literal string to find.
857-
* @param int|false $startPos Position to start at, beginning of string by default.
858-
*
859-
* @return string
855+
* @param string $string SQL expression producing the string to locate the substring in.
856+
* @param string $substring SQL expression producing the substring to locate.
857+
* @param string|null $start SQL expression producing the position to start at.
858+
* Defaults to the beginning of the string.
860859
*
861860
* @throws DBALException If not supported on this platform.
862861
*/
863-
public function getLocateExpression($str, $substr, $startPos = false)
862+
public function getLocateExpression(string $string, string $substring, ?string $start = null) : string
864863
{
865864
throw DBALException::notSupported(__METHOD__);
866865
}
@@ -876,25 +875,22 @@ public function getNowExpression()
876875
}
877876

878877
/**
879-
* Returns a SQL snippet to get a substring inside an SQL statement.
878+
* Returns an SQL snippet to get a substring inside the string.
880879
*
881880
* Note: Not SQL92, but common functionality.
882881
*
883-
* SQLite only supports the 2 parameter variant of this function.
884-
*
885-
* @param string $value An sql string literal or column name/alias.
886-
* @param int $from Where to start the substring portion.
887-
* @param int|null $length The substring portion length.
888-
*
889-
* @return string
882+
* @param string $string SQL expression producing the string from which a substring should be extracted.
883+
* @param string $start SQL expression producing the position to start at,
884+
* @param string|null $length SQL expression producing the length of the substring portion to be returned.
885+
* By default, the entire substring is returned.
890886
*/
891-
public function getSubstringExpression($value, $from, $length = null)
887+
public function getSubstringExpression(string $string, string $start, ?string $length = null) : string
892888
{
893889
if ($length === null) {
894-
return 'SUBSTRING(' . $value . ' FROM ' . $from . ')';
890+
return sprintf('SUBSTRING(%s FROM %s)', $string, $start);
895891
}
896892

897-
return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')';
893+
return sprintf('SUBSTRING(%s FROM %s FOR %s)', $string, $start, $length);
898894
}
899895

900896
/**

lib/Doctrine/DBAL/Platforms/DB2Platform.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -814,25 +814,25 @@ protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) :
814814
/**
815815
* {@inheritDoc}
816816
*/
817-
public function getLocateExpression($str, $substr, $startPos = false)
817+
public function getLocateExpression(string $string, string $substring, ?string $start = null) : string
818818
{
819-
if ($startPos === false) {
820-
return 'LOCATE(' . $substr . ', ' . $str . ')';
819+
if ($start === null) {
820+
return sprintf('LOCATE(%s, %s)', $substring, $string);
821821
}
822822

823-
return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')';
823+
return sprintf('LOCATE(%s, %s, %s)', $substring, $string, $start);
824824
}
825825

826826
/**
827827
* {@inheritDoc}
828828
*/
829-
public function getSubstringExpression($value, $from, $length = null)
829+
public function getSubstringExpression(string $string, string $start, ?string $length = null) : string
830830
{
831831
if ($length === null) {
832-
return 'SUBSTR(' . $value . ', ' . $from . ')';
832+
return sprintf('SUBSTR(%s, %s)', $string, $start);
833833
}
834834

835-
return 'SUBSTR(' . $value . ', ' . $from . ', ' . $length . ')';
835+
return sprintf('SUBSTR(%s, %s, %s)', $string, $start, $length);
836836
}
837837

838838
/**

lib/Doctrine/DBAL/Platforms/MySqlPlatform.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,13 @@ public function getRegexpExpression()
8181
/**
8282
* {@inheritDoc}
8383
*/
84-
public function getLocateExpression($str, $substr, $startPos = false)
84+
public function getLocateExpression(string $string, string $substring, ?string $start = null) : string
8585
{
86-
if ($startPos === false) {
87-
return 'LOCATE(' . $substr . ', ' . $str . ')';
86+
if ($start === null) {
87+
return sprintf('LOCATE(%s, %s)', $substring, $string);
8888
}
8989

90-
return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')';
90+
return sprintf('LOCATE(%s, %s, %s)', $substring, $string, $start);
9191
}
9292

9393
/**

lib/Doctrine/DBAL/Platforms/OraclePlatform.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ public static function assertValidIdentifier($identifier)
4747
/**
4848
* {@inheritDoc}
4949
*/
50-
public function getSubstringExpression($value, $position, $length = null)
50+
public function getSubstringExpression(string $string, string $start, ?string $length = null) : string
5151
{
52-
if ($length !== null) {
53-
return sprintf('SUBSTR(%s, %d, %d)', $value, $position, $length);
52+
if ($length === null) {
53+
return sprintf('SUBSTR(%s, %s)', $string, $start);
5454
}
5555

56-
return sprintf('SUBSTR(%s, %d)', $value, $position);
56+
return sprintf('SUBSTR(%s, %s, %s)', $string, $start, $length);
5757
}
5858

5959
/**
@@ -73,13 +73,13 @@ public function getNowExpression($type = 'timestamp')
7373
/**
7474
* {@inheritDoc}
7575
*/
76-
public function getLocateExpression($str, $substr, $startPos = false)
76+
public function getLocateExpression(string $string, string $substring, ?string $start = null) : string
7777
{
78-
if ($startPos === false) {
79-
return 'INSTR(' . $str . ', ' . $substr . ')';
78+
if ($start === null) {
79+
return sprintf('INSTR(%s, %s)', $string, $substring);
8080
}
8181

82-
return 'INSTR(' . $str . ', ' . $substr . ', ' . $startPos . ')';
82+
return sprintf('INSTR(%s, %s, %s)', $string, $substring, $start);
8383
}
8484

8585
/**

lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,6 @@ public function setUseBooleanTrueFalseStrings($flag)
7575
$this->useBooleanTrueFalseStrings = (bool) $flag;
7676
}
7777

78-
/**
79-
* {@inheritDoc}
80-
*/
81-
public function getSubstringExpression($value, $from, $length = null)
82-
{
83-
if ($length === null) {
84-
return 'SUBSTRING(' . $value . ' FROM ' . $from . ')';
85-
}
86-
87-
return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')';
88-
}
89-
9078
/**
9179
* {@inheritDoc}
9280
*/
@@ -106,15 +94,15 @@ public function getRegexpExpression()
10694
/**
10795
* {@inheritDoc}
10896
*/
109-
public function getLocateExpression($str, $substr, $startPos = false)
97+
public function getLocateExpression(string $string, string $substring, ?string $start = null) : string
11098
{
111-
if ($startPos !== false) {
112-
$str = $this->getSubstringExpression($str, $startPos);
99+
if ($start !== null) {
100+
$string = $this->getSubstringExpression($string, $start);
113101

114-
return 'CASE WHEN (POSITION(' . $substr . ' IN ' . $str . ') = 0) THEN 0 ELSE (POSITION(' . $substr . ' IN ' . $str . ') + ' . ($startPos-1) . ') END';
102+
return 'CASE WHEN (POSITION(' . $substring . ' IN ' . $string . ') = 0) THEN 0 ELSE (POSITION(' . $substring . ' IN ' . $string . ') + ' . $start . ' - 1) END';
115103
}
116104

117-
return 'POSITION(' . $substr . ' IN ' . $str . ')';
105+
return sprintf('POSITION(%s IN %s)', $substring, $string);
118106
}
119107

120108
/**

lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -966,13 +966,13 @@ public function getListViewsSQL($database)
966966
/**
967967
* {@inheritdoc}
968968
*/
969-
public function getLocateExpression($str, $substr, $startPos = false)
969+
public function getLocateExpression(string $string, string $substring, ?string $start = null) : string
970970
{
971-
if ($startPos === false) {
972-
return 'LOCATE(' . $str . ', ' . $substr . ')';
971+
if ($start === null) {
972+
return sprintf('LOCATE(%s, %s)', $string, $substring);
973973
}
974974

975-
return 'LOCATE(' . $str . ', ' . $substr . ', ' . $startPos . ')';
975+
return sprintf('LOCATE(%s, %s, %s)', $string, $substring, $start);
976976
}
977977

978978
/**
@@ -1089,13 +1089,13 @@ public function getStopDatabaseSQL($database)
10891089
/**
10901090
* {@inheritdoc}
10911091
*/
1092-
public function getSubstringExpression($value, $from, $length = null)
1092+
public function getSubstringExpression(string $string, string $start, ?string $length = null) : string
10931093
{
10941094
if ($length === null) {
1095-
return 'SUBSTRING(' . $value . ', ' . $from . ')';
1095+
return sprintf('SUBSTRING(%s, %s)', $string, $start);
10961096
}
10971097

1098-
return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $length . ')';
1098+
return sprintf('SUBSTRING(%s, %s, %s)', $string, $start, $length);
10991099
}
11001100

11011101
/**

lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,13 +1016,13 @@ public function getDropViewSQL($name)
10161016
/**
10171017
* {@inheritDoc}
10181018
*/
1019-
public function getLocateExpression($str, $substr, $startPos = false)
1019+
public function getLocateExpression(string $string, string $substring, ?string $start = null) : string
10201020
{
1021-
if ($startPos === false) {
1022-
return 'CHARINDEX(' . $substr . ', ' . $str . ')';
1021+
if ($start === null) {
1022+
return sprintf('CHARINDEX(%s, %s)', $substring, $string);
10231023
}
10241024

1025-
return 'CHARINDEX(' . $substr . ', ' . $str . ', ' . $startPos . ')';
1025+
return sprintf('CHARINDEX(%s, %s, %s)', $substring, $string, $start);
10261026
}
10271027

10281028
/**
@@ -1108,13 +1108,13 @@ public function getListNamespacesSQL()
11081108
/**
11091109
* {@inheritDoc}
11101110
*/
1111-
public function getSubstringExpression($value, $from, $length = null)
1111+
public function getSubstringExpression(string $string, string $start, ?string $length = null) : string
11121112
{
1113-
if ($length !== null) {
1114-
return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $length . ')';
1113+
if ($length === null) {
1114+
return sprintf('SUBSTRING(%s, %s, LEN(%s) - %s + 1)', $string, $start, $string, $start);
11151115
}
11161116

1117-
return 'SUBSTRING(' . $value . ', ' . $from . ', LEN(' . $value . ') - ' . $from . ' + 1)';
1117+
return sprintf('SUBSTRING(%s, %s, %s)', $string, $start, $length);
11181118
}
11191119

11201120
/**

lib/Doctrine/DBAL/Platforms/SqlitePlatform.php

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -97,28 +97,26 @@ public function getTrimExpression(string $str, int $mode = TrimMode::UNSPECIFIED
9797

9898
/**
9999
* {@inheritDoc}
100-
*
101-
* SQLite only supports the 2 parameter variant of this function
102100
*/
103-
public function getSubstringExpression($value, $position, $length = null)
101+
public function getSubstringExpression(string $string, string $start, ?string $length = null) : string
104102
{
105-
if ($length !== null) {
106-
return 'SUBSTR(' . $value . ', ' . $position . ', ' . $length . ')';
103+
if ($length === null) {
104+
return sprintf('SUBSTR(%s, %s)', $string, $start);
107105
}
108106

109-
return 'SUBSTR(' . $value . ', ' . $position . ', LENGTH(' . $value . '))';
107+
return sprintf('SUBSTR(%s, %s, %s)', $string, $start, $length);
110108
}
111109

112110
/**
113111
* {@inheritDoc}
114112
*/
115-
public function getLocateExpression($str, $substr, $startPos = false)
113+
public function getLocateExpression(string $string, string $substring, ?string $start = null) : string
116114
{
117-
if ($startPos === false) {
118-
return 'LOCATE(' . $str . ', ' . $substr . ')';
115+
if ($start === null) {
116+
return sprintf('LOCATE(%s, %s)', $string, $substring);
119117
}
120118

121-
return 'LOCATE(' . $str . ', ' . $substr . ', ' . $startPos . ')';
119+
return sprintf('LOCATE(%s, %s, %s)', $string, $substring, $start);
122120
}
123121

124122
/**

tests/Doctrine/Tests/DBAL/Functional/DataAccessTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,47 @@ public function testLocateExpression() : void
679679
self::assertEquals(0, $row['locate9']);
680680
}
681681

682+
/**
683+
* @dataProvider substringExpressionProvider
684+
*/
685+
public function testSubstringExpression(string $string, string $start, ?string $length, string $expected) : void
686+
{
687+
$platform = $this->connection->getDatabasePlatform();
688+
689+
$query = $platform->getDummySelectSQL(
690+
$platform->getSubstringExpression($string, $start, $length)
691+
);
692+
693+
$this->assertEquals($expected, $this->connection->fetchColumn($query));
694+
}
695+
696+
/**
697+
* @return mixed[][]
698+
*/
699+
public static function substringExpressionProvider() : iterable
700+
{
701+
return [
702+
'start-no-length' => [
703+
"'abcdef'",
704+
'3',
705+
null,
706+
'cdef',
707+
],
708+
'start-with-length' => [
709+
"'abcdef'",
710+
'2',
711+
'4',
712+
'bcde',
713+
],
714+
'expressions' => [
715+
"'abcdef'",
716+
'1 + 1',
717+
'1 + 1',
718+
'bc',
719+
],
720+
];
721+
}
722+
682723
public function testQuoteSQLInjection() : void
683724
{
684725
$sql = 'SELECT * FROM fetch_table WHERE test_string = ' . $this->connection->quote("bar' OR '1'='1");

tests/Doctrine/Tests/DBAL/Platforms/DB2PlatformTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,6 @@ public function testGeneratesSQLSnippets() : void
363363
self::assertEquals("'1987/05/02' - 10 YEAR", $this->platform->getDateSubYearsExpression("'1987/05/02'", 10));
364364
self::assertEquals(' WITH RR USE AND KEEP UPDATE LOCKS', $this->platform->getForUpdateSQL());
365365
self::assertEquals('LOCATE(substring_column, string_column)', $this->platform->getLocateExpression('string_column', 'substring_column'));
366-
self::assertEquals('LOCATE(substring_column, string_column)', $this->platform->getLocateExpression('string_column', 'substring_column'));
367366
self::assertEquals('LOCATE(substring_column, string_column, 1)', $this->platform->getLocateExpression('string_column', 'substring_column', 1));
368367
self::assertEquals('SUBSTR(column, 5)', $this->platform->getSubstringExpression('column', 5));
369368
self::assertEquals('SUBSTR(column, 5, 2)', $this->platform->getSubstringExpression('column', 5, 2));

tests/Doctrine/Tests/DBAL/Platforms/SqlitePlatformTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function getGenerateTableWithMultiColumnUniqueIndexSql() : array
3737
public function testGeneratesSqlSnippets() : void
3838
{
3939
self::assertEquals('REGEXP', $this->platform->getRegexpExpression(), 'Regular expression operator is not correct');
40-
self::assertEquals('SUBSTR(column, 5, LENGTH(column))', $this->platform->getSubstringExpression('column', 5), 'Substring expression without length is not correct');
40+
self::assertEquals('SUBSTR(column, 5)', $this->platform->getSubstringExpression('column', 5), 'Substring expression without length is not correct');
4141
self::assertEquals('SUBSTR(column, 0, 5)', $this->platform->getSubstringExpression('column', 0, 5), 'Substring expression with length is not correct');
4242
}
4343

0 commit comments

Comments
 (0)