KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > h2 > expression > CompareLike


1 /*
2  * Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
3  * Initial Developer: H2 Group
4  */

5 package org.h2.expression;
6
7 import java.sql.SQLException JavaDoc;
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 /**
23  * @author Thomas
24  */

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 JavaDoc 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 JavaDoc getSQL() {
49         String JavaDoc 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 JavaDoc {
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                 // NULL LIKE something > NULL
66
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                 // something LIKE NULL > NULL
79
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 JavaDoc 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 JavaDoc {
93         if(e == null) {
94             return Constants.DEFAULT_ESCAPE_CHAR;
95         }
96         String JavaDoc 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 JavaDoc {
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         // parameters are always evaluatable, but
116
// we need to check the actual value now
117
// (at prepare time)
118
// otherwise we would need to prepare at execute time,
119
// which is maybe slower (but maybe not in this case!)
120
// TODO optimizer: like: check what other databases do!
121
if(!right.isConstant()) {
122             return;
123         }
124         if(escape != null && !escape.isConstant()) {
125             return;
126         }
127         String JavaDoc p = right.getValue(session).getString();
128         Value e = escape == null ? null : escape.getValue(session);
129         if(e == ValueNull.INSTANCE) {
130             // should already be optimized
131
throw Message.getInternalError();
132         }
133         initPattern(p, getEscapeChar(e));
134         if(patternLength <= 0 || types[0] != MATCH) {
135             // can't use an index
136
return;
137         }
138         int dataType = l.getColumn().getType();
139         if(dataType != Value.STRING && dataType != Value.STRING_IGNORECASE) {
140             // column is not a varchar - can't use the index
141
return;
142         }
143         int maxMatch = 0;
144         StringBuffer JavaDoc buff = new StringBuffer JavaDoc();
145         while(maxMatch < patternLength && types[maxMatch] == MATCH) {
146             buff.append(pattern[maxMatch++]);
147         }
148         String JavaDoc begin = buff.toString();
149         if(maxMatch == patternLength) {
150             filter.addIndexCondition(new IndexCondition(Comparison.EQUAL, l, ValueExpression.get(ValueString.get(begin))));
151         } else {
152             // TODO check if this is correct according to Unicode rules (codepoints)
153
String JavaDoc 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                 // search the 'next' unicode character (or at least a character that is higher)
158
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 JavaDoc {
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 JavaDoc 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 JavaDoc value = l.getString();
187         boolean result = compareAt(value, 0, 0, value.length());
188         return ValueBoolean.get(result);
189     }
190
191     private boolean compare(String JavaDoc s, int pi, int si) {
192         // TODO check if this is correct according to Unicode rules (codepoints)
193
return compareMode.compareString(patternString.substring(pi, pi+1), s.substring(si, si+1), ignoreCase) == 0;
194     }
195
196     private boolean compareAt(String JavaDoc 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 JavaDoc pattern, String JavaDoc value, char escape) throws SQLException JavaDoc {
229         initPattern(pattern, escape);
230         return compareAt(value, 0, 0, value.length());
231     }
232
233     private void initPattern(String JavaDoc p, char escape) throws SQLException JavaDoc {
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 JavaDoc(pattern);
278     }
279
280     public void mapColumns(ColumnResolver resolver, int level) throws SQLException JavaDoc {
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 JavaDoc {
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