Skip to content

Commit a8fbf8a

Browse files
committed
[BUGFIX] Expression in like escape % and _
The Expression LIKE binary operator does not care about escape % and _ char. No-one has already open an issue about it but in the OGC element PropertyIsLike the user can defined is own wild and single char. This mean that QGIS has to escape % and _ if they are not used as wild and single char.
1 parent e146fce commit a8fbf8a

File tree

4 files changed

+60
-21
lines changed

4 files changed

+60
-21
lines changed

resources/function_help/json/ILIKE

+15-9
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,22 @@
33
"type": "operator",
44
"description": "Returns 1 if the first parameter matches case-insensitive the supplied pattern. LIKE can be used instead of ILIKE to make the match case-sensitive. Works with numbers also.",
55
"arguments": [
6-
{"arg":"string/number","description":"string to search"},
7-
{"arg":"pattern","description":"pattern to find"}
6+
{"arg":"string/number","description":"string to search"},
7+
{"arg":"pattern","description":"pattern to find, you can use '%' as a wildcard, '_' as a single char and '\\\\' to escape."}
88
],
99
"examples": [
10-
{ "expression":"'A' ILIKE 'A'", "returns":"1"},
11-
{ "expression":"'A' ILIKE 'a'", "returns":"1"},
12-
{ "expression":"'A' ILIKE 'B'", "returns":"0"},
13-
{ "expression":"'ABC' ILIKE 'b'", "returns":"0"},
14-
{ "expression":"'ABC' ILIKE 'B'", "returns":"0"},
15-
{ "expression":"'ABC' ILIKE '%b%'", "returns":"1"},
16-
{ "expression":"'ABC' ILIKE '%B%'", "returns":"1"}
10+
{ "expression":"'A' ILIKE 'A'", "returns":"1"},
11+
{ "expression":"'A' ILIKE 'a'", "returns":"1"},
12+
{ "expression":"'A' ILIKE 'B'", "returns":"0"},
13+
{ "expression":"'ABC' ILIKE 'b'", "returns":"0"},
14+
{ "expression":"'ABC' ILIKE 'B'", "returns":"0"},
15+
{ "expression":"'ABC' ILIKE '_b_'", "returns":"1"},
16+
{ "expression":"'ABC' ILIKE '_B_'", "returns":"1"},
17+
{ "expression":"'ABCD' ILIKE '_b_'", "returns":"0"},
18+
{ "expression":"'ABCD' ILIKE '_B_'", "returns":"0"},
19+
{ "expression":"'ABCD' ILIKE '_b%'", "returns":"1"},
20+
{ "expression":"'ABCD' ILIKE '_B%'", "returns":"1"},
21+
{ "expression":"'ABCD' ILIKE '%b%'", "returns":"1"},
22+
{ "expression":"'ABCD' ILIKE '%B%'", "returns":"1"}
1723
]
1824
}

resources/function_help/json/LIKE

+12-7
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@
33
"type": "operator",
44
"description": "Returns 1 if the first parameter matches the supplied pattern. Works with numbers also.",
55
"arguments": [
6-
{"arg":"string/number","description":"value"},
7-
{"arg":"pattern","description":"pattern to compare value with"}
6+
{"arg":"string/number","description":"value"},
7+
{"arg":"pattern","description":"pattern to compare value with, you can use '%' as a wildcard, '_' as a single char and '\\\\' to escape."}
88
],
99
"examples": [
10-
{ "expression":"'A' LIKE 'A'", "returns":"1"},
11-
{ "expression":"'A' LIKE 'a'", "returns":"0"},
12-
{ "expression":"'A' LIKE 'B'", "returns":"0"},
13-
{ "expression":"'ABC' LIKE 'B'", "returns":"0"},
14-
{ "expression":"'ABC' LIKE '%B%'", "returns":"1"}
10+
{ "expression":"'A' LIKE 'A'", "returns":"1"},
11+
{ "expression":"'A' LIKE 'a'", "returns":"0"},
12+
{ "expression":"'A' LIKE 'B'", "returns":"0"},
13+
{ "expression":"'ABC' LIKE 'B'", "returns":"0"},
14+
{ "expression":"'ABC' LIKE '_B_'", "returns":"1"},
15+
{ "expression":"'ABCD' LIKE '_B_'", "returns":"0"},
16+
{ "expression":"'ABCD' LIKE '_B%'", "returns":"1"},
17+
{ "expression":"'ABCD' LIKE '%B%'", "returns":"1"},
18+
{ "expression":"'1%' LIKE '1\\\\%'", "returns":"1"},
19+
{ "expression":"'1_' LIKE '1\\\\%'", "returns":"0"}
1520
]
1621
}

src/core/qgsexpression.cpp

+27-3
Original file line numberDiff line numberDiff line change
@@ -4343,9 +4343,33 @@ QVariant QgsExpression::NodeBinaryOperator::eval( QgsExpression *parent, const Q
43434343
if ( mOp == boLike || mOp == boILike || mOp == boNotLike || mOp == boNotILike ) // change from LIKE syntax to regexp
43444344
{
43454345
QString esc_regexp = QRegExp::escape( regexp );
4346-
// XXX escape % and _ ???
4347-
esc_regexp.replace( '%', ".*" );
4348-
esc_regexp.replace( '_', '.' );
4346+
// manage escape % and _
4347+
if ( esc_regexp.startsWith( '%' ) )
4348+
{
4349+
esc_regexp.replace( 0, 1, ".*" );
4350+
}
4351+
QRegExp rx( "[^\\\\](%)" );
4352+
int pos = 0;
4353+
while (( pos = rx.indexIn( esc_regexp, pos ) ) != -1 )
4354+
{
4355+
esc_regexp.replace( pos + 1, 1, ".*" );
4356+
pos += 1;
4357+
}
4358+
rx.setPattern( "\\\\%" );
4359+
esc_regexp.replace( rx, "%" );
4360+
if ( esc_regexp.startsWith( '_' ) )
4361+
{
4362+
esc_regexp.replace( 0, 1, "." );
4363+
}
4364+
rx.setPattern( "[^\\\\](_)" );
4365+
pos = 0;
4366+
while (( pos = rx.indexIn( esc_regexp, pos ) ) != -1 )
4367+
{
4368+
esc_regexp.replace( pos + 1, 1, '.' );
4369+
pos += 1;
4370+
}
4371+
rx.setPattern( "\\\\_" );
4372+
esc_regexp.replace( rx, "_" );
43494373
matches = QRegExp( esc_regexp, mOp == boLike || mOp == boNotLike ? Qt::CaseSensitive : Qt::CaseInsensitive ).exactMatch( str );
43504374
}
43514375
else

tests/src/core/testqgsexpression.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -482,9 +482,13 @@ class TestQgsExpression: public QObject
482482

483483
// regexp, like
484484
QTest::newRow( "like 1" ) << "'hello' like '%ll_'" << false << QVariant( 1 );
485-
QTest::newRow( "like 2" ) << "'hello' like 'lo'" << false << QVariant( 0 );
486-
QTest::newRow( "like 3" ) << "'hello' like '%LO'" << false << QVariant( 0 );
485+
QTest::newRow( "like 2" ) << "'hello' like '_el%'" << false << QVariant( 1 );
486+
QTest::newRow( "like 3" ) << "'hello' like 'lo'" << false << QVariant( 0 );
487+
QTest::newRow( "like 4" ) << "'hello' like '%LO'" << false << QVariant( 0 );
487488
QTest::newRow( "ilike" ) << "'hello' ilike '%LO'" << false << QVariant( 1 );
489+
// the \\\\ is like \\ in the interface
490+
QTest::newRow( "like escape 1" ) << "'1%' like '1\\\\%'" << false << QVariant( 1 );
491+
QTest::newRow( "like escape 2" ) << "'1_' like '1\\\\%'" << false << QVariant( 0 );
488492
QTest::newRow( "regexp 1" ) << "'hello' ~ 'll'" << false << QVariant( 1 );
489493
QTest::newRow( "regexp 2" ) << "'hello' ~ '^ll'" << false << QVariant( 0 );
490494
QTest::newRow( "regexp 3" ) << "'hello' ~ 'llo$'" << false << QVariant( 1 );

0 commit comments

Comments
 (0)