Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/tests/operators.queries.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,34 @@ describe('Operators', () => {
]);
});

it('executes like with an escaped percent sign', () => {
expect(many(`create table test(val text);
insert into test values ('a%b'), ('axb'), ('a_b'), (null);
select * from test where val like 'a\\%b'`))
.toEqual([
{ val: 'a%b' }
]);
});

it('executes like with an escaped underscore', () => {
expect(many(`create table test(val text);
insert into test values ('a_b'), ('axb'), ('a%b'), (null);
select * from test where val like 'a\\_b'`))
.toEqual([
{ val: 'a_b' }
]);
});

it('executes like with an escaped percent sign on an indexed column', () => {
expect(many(`create table test(val text);
create index on test(val);
insert into test values ('a%b'), ('axb'), ('a_b'), (null);
select * from test where val like 'a\\%b'`))
.toEqual([
{ val: 'a%b' }
]);
});


it('executes not like', () => {
expect(many(`create table test(val text);
Expand Down
3 changes: 2 additions & 1 deletion src/transforms/build-filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ function buildBinaryFilter(this: void, on: _ISelection, filter: ExprBinary): _IS
if (nullIsh(str)) {
return new FalseFilter(on);
}
const got = /^([^%_]+)([%_]?.+)$/.exec(str);
const got = String(str).indexOf('\\') === -1
&& /^([^%_]+)([%_]?.+)$/.exec(str);
if (got) {
const start = got[1];
if (start.length === str) {
Expand Down
23 changes: 19 additions & 4 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,11 +290,26 @@ export function queryJson(a: Json, b: Json) {
return true;
}

function escapeRegexChar(char: string): string {
return char.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}

export function buildLikeMatcher(likeCondition: string, caseSensitive = true) {
// Escape regex characters from likeCondition
likeCondition = likeCondition.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
let likeRegexString = likeCondition.replace(/\%/g, ".*").replace(/_/g, '.');
likeRegexString = "^" + likeRegexString + "$";
let likeRegexString = '^';
for (let i = 0; i < likeCondition.length; i++) {
const char = likeCondition[i];
if (char === '\\') {
const escaped = likeCondition[++i];
likeRegexString += escaped === undefined ? '\\\\' : escapeRegexChar(escaped);
} else if (char === '%') {
likeRegexString += '.*';
} else if (char === '_') {
likeRegexString += '.';
} else {
likeRegexString += escapeRegexChar(char);
}
}
likeRegexString += '$';
const reg = new RegExp(likeRegexString, caseSensitive ? '' : 'i');

return (stringToMatch: string | number) => {
Expand Down