1 5 package org.h2.expression; 6 7 import java.sql.SQLException ; 8 9 import org.h2.engine.Constants; 10 import org.h2.engine.Session; 11 import org.h2.index.IndexCondition; 12 import org.h2.message.Message; 13 import org.h2.table.ColumnResolver; 14 import org.h2.table.TableFilter; 15 import org.h2.util.StringUtils; 16 import org.h2.value.CompareMode; 17 import org.h2.value.Value; 18 import org.h2.value.ValueBoolean; 19 import org.h2.value.ValueNull; 20 import org.h2.value.ValueString; 21 22 25 26 public class CompareLike extends Condition { 27 28 private CompareMode compareMode; 29 private Expression left; 30 private Expression right; 31 private Expression escape; 32 33 private boolean isInit; 34 private char[] pattern; 35 private String patternString; 36 private int[] types; 37 private int patternLength; 38 private static final int MATCH = 0, ONE = 1, ANY = 2; 39 private boolean ignoreCase; 40 41 public CompareLike(CompareMode compareMode, Expression left, Expression right, Expression escape) { 42 this.compareMode = compareMode; 43 this.left = left; 44 this.right = right; 45 this.escape = escape; 46 } 47 48 public String getSQL() { 49 String sql = left.getSQL() + " LIKE " + right.getSQL(); 50 if (escape != null) { 51 sql += " ESCAPE " + escape.getSQL(); 52 } 53 return "("+sql+")"; 54 } 55 56 public Expression optimize(Session session) throws SQLException { 57 left = left.optimize(session); 58 right = right.optimize(session); 59 if(left.getType() == Value.STRING_IGNORECASE) { 60 ignoreCase = true; 61 } 62 if(left.isConstant()) { 63 Value l = left.getValue(session); 64 if (l == ValueNull.INSTANCE) { 65 return ValueExpression.NULL; 67 } 68 } 69 if(escape != null) { 70 escape = escape.optimize(session); 71 } 72 if(right.isConstant() && (escape == null || escape.isConstant())) { 73 if(left.isConstant()) { 74 return ValueExpression.get(getValue(session)); 75 } 76 Value r = right.getValue(session); 77 if (r == ValueNull.INSTANCE) { 78 return ValueExpression.NULL; 80 } 81 Value e = escape == null ? null : escape.getValue(session); 82 if(e == ValueNull.INSTANCE) { 83 return ValueExpression.NULL; 84 } 85 String pattern = r.getString(); 86 initPattern(pattern, getEscapeChar(e)); 87 isInit = true; 88 } 89 return this; 90 } 91 92 private char getEscapeChar(Value e) throws SQLException { 93 if(e == null) { 94 return Constants.DEFAULT_ESCAPE_CHAR; 95 } 96 String es = e.getString(); 97 char esc; 98 if(es == null || es.length() == 0) { 99 esc = Constants.DEFAULT_ESCAPE_CHAR; 100 } else { 101 esc = es.charAt(0); 102 } 103 return esc; 104 } 105 106 public void createIndexConditions(TableFilter filter) throws SQLException { 107 Session session = filter.getSession(); 108 if(!(left instanceof ExpressionColumn)) { 109 return; 110 } 111 ExpressionColumn l = (ExpressionColumn)left; 112 if(filter != l.getTableFilter()) { 113 return; 114 } 115 if(!right.isConstant()) { 122 return; 123 } 124 if(escape != null && !escape.isConstant()) { 125 return; 126 } 127 String p = right.getValue(session).getString(); 128 Value e = escape == null ? null : escape.getValue(session); 129 if(e == ValueNull.INSTANCE) { 130 throw Message.getInternalError(); 132 } 133 initPattern(p, getEscapeChar(e)); 134 if(patternLength <= 0 || types[0] != MATCH) { 135 return; 137 } 138 int dataType = l.getColumn().getType(); 139 if(dataType != Value.STRING && dataType != Value.STRING_IGNORECASE) { 140 return; 142 } 143 int maxMatch = 0; 144 StringBuffer buff = new StringBuffer (); 145 while(maxMatch < patternLength && types[maxMatch] == MATCH) { 146 buff.append(pattern[maxMatch++]); 147 } 148 String begin = buff.toString(); 149 if(maxMatch == patternLength) { 150 filter.addIndexCondition(new IndexCondition(Comparison.EQUAL, l, ValueExpression.get(ValueString.get(begin)))); 151 } else { 152 String end; 154 if(begin.length()>0) { 155 filter.addIndexCondition(new IndexCondition(Comparison.BIGGER_EQUAL, l, ValueExpression.get(ValueString.get(begin)))); 156 char next = begin.charAt(begin.length()-1); 157 for(int i=1; i<2000; i++) { 159 end = begin.substring(0, begin.length()-1) + (char)(next+i); 160 if(compareMode.compareString(begin, end, ignoreCase) == -1) { 161 filter.addIndexCondition(new IndexCondition(Comparison.SMALLER, l, ValueExpression.get(ValueString.get(end)))); 162 break; 163 } 164 } 165 } 166 } 167 } 168 169 public Value getValue(Session session) throws SQLException { 170 Value l = left.getValue(session); 171 if (l == ValueNull.INSTANCE) { 172 return l; 173 } 174 if(!isInit) { 175 Value r = right.getValue(session); 176 if (r == ValueNull.INSTANCE) { 177 return r; 178 } 179 String pattern = r.getString(); 180 Value e = escape == null ? null : escape.getValue(session); 181 if(e == ValueNull.INSTANCE) { 182 return ValueNull.INSTANCE; 183 } 184 initPattern(pattern, getEscapeChar(e)); 185 } 186 String value = l.getString(); 187 boolean result = compareAt(value, 0, 0, value.length()); 188 return ValueBoolean.get(result); 189 } 190 191 private boolean compare(String s, int pi, int si) { 192 return compareMode.compareString(patternString.substring(pi, pi+1), s.substring(si, si+1), ignoreCase) == 0; 194 } 195 196 private boolean compareAt(String s, int pi, int si, int slen) { 197 for (; pi < patternLength; pi++) { 198 int type = types[pi]; 199 switch (type) { 200 case MATCH: 201 if ((si >= slen) || !compare(s, pi, si++)) { 202 return false; 203 } 204 break; 205 case ONE: 206 if (si++ >= slen) { 207 return false; 208 } 209 break; 210 case ANY: 211 if (++pi >= patternLength) { 212 return true; 213 } 214 while (si < slen) { 215 if (compare(s, pi, si) && compareAt(s, pi, si, slen)) { 216 return true; 217 } 218 si++; 219 } 220 return false; 221 default: 222 throw Message.getInternalError("type="+type); 223 } 224 } 225 return si==slen; 226 } 227 228 public boolean test(String pattern, String value, char escape) throws SQLException { 229 initPattern(pattern, escape); 230 return compareAt(value, 0, 0, value.length()); 231 } 232 233 private void initPattern(String p, char escape) throws SQLException { 234 patternLength = 0; 235 if(p == null) { 236 types = null; 237 pattern = null; 238 return; 239 } 240 int len = p.length(); 241 pattern = new char[len]; 242 types = new int[len]; 243 boolean lastAny = false; 244 for (int i = 0; i < len; i++) { 245 char c = p.charAt(i); 246 int type; 247 if (escape == c) { 248 if (i >= len - 1) { 249 throw Message.getSQLException(Message.LIKE_ESCAPE_ERROR_1, StringUtils.addAsterix(p, i)); 250 } 251 c = p.charAt(++i); 252 if(c != '_' && c != '%' && c != escape) { 253 throw Message.getSQLException(Message.LIKE_ESCAPE_ERROR_1, StringUtils.addAsterix(p, i)); 254 } 255 type = MATCH; 256 } else if (c == '%') { 257 if(lastAny) { 258 continue; 259 } 260 type = ANY; 261 lastAny = true; 262 } else if (c == '_') { 263 type = ONE; 264 } else { 265 type = MATCH; 266 lastAny = false; 267 } 268 types[patternLength] = type; 269 pattern[patternLength++] = c; 270 } 271 for (int i = 0; i < patternLength - 1; i++) { 272 if ((types[i] == ANY) && (types[i + 1] == ONE)) { 273 types[i] = ONE; 274 types[i + 1] = ANY; 275 } 276 } 277 patternString = new String (pattern); 278 } 279 280 public void mapColumns(ColumnResolver resolver, int level) throws SQLException { 281 left.mapColumns(resolver, level); 282 right.mapColumns(resolver, level); 283 if(escape != null) { 284 escape.mapColumns(resolver, level); 285 } 286 } 287 288 public void setEvaluatable(TableFilter tableFilter, boolean b) { 289 left.setEvaluatable(tableFilter, b); 290 right.setEvaluatable(tableFilter, b); 291 if(escape != null) { 292 escape.setEvaluatable(tableFilter, b); 293 } 294 } 295 296 public void updateAggregate(Session session) throws SQLException { 297 left.updateAggregate(session); 298 right.updateAggregate(session); 299 if(escape != null) { 300 escape.updateAggregate(session); 301 } 302 } 303 304 public boolean isEverything(ExpressionVisitor visitor) { 305 return left.isEverything(visitor) && right.isEverything(visitor) && (escape== null || escape.isEverything(visitor)); 306 } 307 308 public int getCost() { 309 return left.getCost() + right.getCost() + 3; 310 } 311 312 } 313 | Popular Tags |