提交 eed8f2ba authored 作者: Noel Grandin's avatar Noel Grandin

Optimise LIKE queries for the common cases of '%Foo' and '%Foo%'

上级 fe535f21
...@@ -21,6 +21,8 @@ Change Log ...@@ -21,6 +21,8 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>Optimise LIKE queries for the common cases of '%Foo' and '%Foo%'
</li>
<li>Issue #387: H2 MSSQL Compatibility Mode - Support uniqueidentifier <li>Issue #387: H2 MSSQL Compatibility Mode - Support uniqueidentifier
</li> </li>
<li>Issue #401: NPE in "SELECT DISTINCT * ORDER BY" <li>Issue #401: NPE in "SELECT DISTINCT * ORDER BY"
......
...@@ -49,6 +49,10 @@ public class CompareLike extends Condition { ...@@ -49,6 +49,10 @@ public class CompareLike extends Condition {
private boolean invalidPattern; private boolean invalidPattern;
/** indicates that we can shortcut the comparison and use startsWith */ /** indicates that we can shortcut the comparison and use startsWith */
private boolean shortcutToStartsWith; private boolean shortcutToStartsWith;
/** indicates that we can shortcut the comparison and use endsWith */
private boolean shortcutToEndsWith;
/** indicates that we can shortcut the comparison and use contains */
private boolean shortcutToContains;
public CompareLike(Database db, Expression left, Expression right, public CompareLike(Database db, Expression left, Expression right,
Expression escape, boolean regexp) { Expression escape, boolean regexp) {
...@@ -179,12 +183,14 @@ public class CompareLike extends Condition { ...@@ -179,12 +183,14 @@ public class CompareLike extends Condition {
return; return;
} }
String p = right.getValue(session).getString(); String p = right.getValue(session).getString();
Value e = escape == null ? null : escape.getValue(session); if (!isInit) {
if (e == ValueNull.INSTANCE) { Value e = escape == null ? null : escape.getValue(session);
// should already be optimized if (e == ValueNull.INSTANCE) {
DbException.throwInternalError(); // should already be optimized
DbException.throwInternalError();
}
initPattern(p, getEscapeChar(e));
} }
initPattern(p, getEscapeChar(e));
if (invalidPattern) { if (invalidPattern) {
return; return;
} }
...@@ -260,12 +266,42 @@ public class CompareLike extends Condition { ...@@ -260,12 +266,42 @@ public class CompareLike extends Condition {
result = patternRegexp.matcher(value).find(); result = patternRegexp.matcher(value).find();
} else if (shortcutToStartsWith) { } else if (shortcutToStartsWith) {
result = value.regionMatches(ignoreCase, 0, patternString, 0, patternLength - 1); result = value.regionMatches(ignoreCase, 0, patternString, 0, patternLength - 1);
} else if (shortcutToEndsWith) {
result = value.regionMatches(ignoreCase, value.length() - patternLength + 1, patternString, 1, patternLength - 1);
} else if (shortcutToContains) {
String p = patternString.substring(1, patternString.length() - 1);
if (ignoreCase) {
result = containsIgnoreCase(value, p);
} else {
result = value.contains(p);
}
} else { } else {
result = compareAt(value, 0, 0, value.length(), patternChars, patternTypes); result = compareAt(value, 0, 0, value.length(), patternChars, patternTypes);
} }
return ValueBoolean.get(result); return ValueBoolean.get(result);
} }
private static boolean containsIgnoreCase(String src, String what) {
final int length = what.length();
if (length == 0)
return true; // Empty string is contained
final char firstLo = Character.toLowerCase(what.charAt(0));
final char firstUp = Character.toUpperCase(what.charAt(0));
for (int i = src.length() - length; i >= 0; i--) {
// Quick check before calling the more expensive regionMatches() method:
final char ch = src.charAt(i);
if (ch != firstLo && ch != firstUp)
continue;
if (src.regionMatches(true, i, what, 0, length))
return true;
}
return false;
}
private boolean compareAt(String s, int pi, int si, int sLen, private boolean compareAt(String s, int pi, int si, int sLen,
char[] pattern, int[] types) { char[] pattern, int[] types) {
for (; pi < patternLength; pi++) { for (; pi < patternLength; pi++) {
...@@ -390,6 +426,32 @@ public class CompareLike extends Condition { ...@@ -390,6 +426,32 @@ public class CompareLike extends Condition {
} }
if (maxMatch == patternLength - 1 && patternTypes[patternLength - 1] == ANY) { if (maxMatch == patternLength - 1 && patternTypes[patternLength - 1] == ANY) {
shortcutToStartsWith = true; shortcutToStartsWith = true;
return;
}
}
// optimizes the common case of LIKE '%foo'
if (compareMode.getName().equals(CompareMode.OFF) && patternLength > 1) {
if (patternTypes[0] == ANY) {
int maxMatch = 1;
while (maxMatch < patternLength && patternTypes[maxMatch] == MATCH) {
maxMatch++;
}
if (maxMatch == patternLength) {
shortcutToEndsWith = true;
return;
}
}
}
// optimizes the common case of LIKE '%foo%'
if (compareMode.getName().equals(CompareMode.OFF) && patternLength > 2) {
if (patternTypes[0] == ANY) {
int maxMatch = 1;
while (maxMatch < patternLength && patternTypes[maxMatch] == MATCH) {
maxMatch++;
}
if (maxMatch == patternLength - 1 && patternTypes[patternLength - 1] == ANY) {
shortcutToContains = true;
}
} }
} }
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论