001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 2008.
003: *
004: * Licensed under the Aduna BSD-style license.
005: */
006: package org.openrdf.sail.rdbms.optimizers;
007:
008: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.and;
009: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.isNull;
010: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.not;
011: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.or;
012: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.str;
013:
014: import java.util.List;
015: import java.util.Locale;
016:
017: import org.openrdf.query.BindingSet;
018: import org.openrdf.query.Dataset;
019: import org.openrdf.query.algebra.QueryModelNode;
020: import org.openrdf.query.algebra.TupleExpr;
021: import org.openrdf.query.algebra.evaluation.QueryOptimizer;
022: import org.openrdf.sail.rdbms.algebra.FalseValue;
023: import org.openrdf.sail.rdbms.algebra.SelectQuery;
024: import org.openrdf.sail.rdbms.algebra.SqlAnd;
025: import org.openrdf.sail.rdbms.algebra.SqlCase;
026: import org.openrdf.sail.rdbms.algebra.SqlCompare;
027: import org.openrdf.sail.rdbms.algebra.SqlConcat;
028: import org.openrdf.sail.rdbms.algebra.SqlEq;
029: import org.openrdf.sail.rdbms.algebra.SqlIsNull;
030: import org.openrdf.sail.rdbms.algebra.SqlLowerCase;
031: import org.openrdf.sail.rdbms.algebra.SqlNot;
032: import org.openrdf.sail.rdbms.algebra.SqlNull;
033: import org.openrdf.sail.rdbms.algebra.SqlOr;
034: import org.openrdf.sail.rdbms.algebra.StringValue;
035: import org.openrdf.sail.rdbms.algebra.TrueValue;
036: import org.openrdf.sail.rdbms.algebra.SqlCase.Entry;
037: import org.openrdf.sail.rdbms.algebra.base.BinarySqlOperator;
038: import org.openrdf.sail.rdbms.algebra.base.FromItem;
039: import org.openrdf.sail.rdbms.algebra.base.RdbmsQueryModelVisitorBase;
040: import org.openrdf.sail.rdbms.algebra.base.SqlConstant;
041: import org.openrdf.sail.rdbms.algebra.base.SqlExpr;
042: import org.openrdf.sail.rdbms.algebra.base.UnarySqlOperator;
043:
044: /**
045: * Optimises SQL constants, include operations with static values and null
046: * operations.
047: *
048: * @author James Leigh
049: *
050: */
051: public class SqlConstantOptimizer extends
052: RdbmsQueryModelVisitorBase<RuntimeException> implements
053: QueryOptimizer {
054:
055: @Override
056: public void meet(SelectQuery node) throws RuntimeException {
057: super .meet(node);
058: List<SqlExpr> filters = node.getFilters();
059: for (int i = filters.size() - 1; i >= 0; i--) {
060: if (filters.get(i) instanceof TrueValue) {
061: node.removeFilter(filters.get(i));
062: }
063: }
064: }
065:
066: @Override
067: public void meet(SqlAnd node) throws RuntimeException {
068: super .meet(node);
069: SqlExpr left = node.getLeftArg();
070: SqlExpr right = node.getRightArg();
071: if (left instanceof FalseValue || right instanceof FalseValue) {
072: replace(node, new FalseValue());
073: } else if (left instanceof TrueValue
074: && right instanceof TrueValue) {
075: replace(node, new TrueValue());
076: } else if (left instanceof TrueValue) {
077: replace(node, right.clone());
078: } else if (right instanceof TrueValue) {
079: replace(node, left.clone());
080: } else if (right instanceof SqlNull || left instanceof SqlNull) {
081: replace(node, new SqlNull());
082: } else if (right instanceof SqlNot
083: && ((SqlNot) right).getArg().equals(left)) {
084: replace(node, new FalseValue());
085: } else if (left instanceof SqlNot
086: && ((SqlNot) left).getArg().equals(right)) {
087: replace(node, new FalseValue());
088: }
089: }
090:
091: @Override
092: public void meet(SqlCase node) throws RuntimeException {
093: super .meet(node);
094: List<Entry> entries = node.getEntries();
095: for (SqlCase.Entry e : entries) {
096: if (e.getCondition() instanceof SqlNull) {
097: node.removeEntry(e);
098: } else if (e.getCondition() instanceof FalseValue) {
099: node.removeEntry(e);
100: } else if (e.getCondition() instanceof TrueValue) {
101: node.truncateEntries(e);
102: break;
103: }
104: }
105: entries = node.getEntries();
106: if (entries.isEmpty()) {
107: replace(node, new SqlNull());
108: } else if (entries.size() == 1) {
109: Entry entry = entries.get(0);
110: if (entry.getCondition() instanceof TrueValue) {
111: replace(node, entry.getResult().clone());
112: } else if (entry.getCondition() instanceof FalseValue) {
113: replace(node, new SqlNull());
114: } else if (entry.getCondition() instanceof SqlNot) {
115: SqlNot not = (SqlNot) entry.getCondition();
116: if (not.getArg() instanceof SqlIsNull) {
117: SqlIsNull is = (SqlIsNull) not.getArg();
118: if (is.getArg().equals(entry.getResult())) {
119: replace(node, entry.getResult().clone());
120: }
121: }
122: }
123: }
124: }
125:
126: @Override
127: public void meet(SqlCompare node) throws RuntimeException {
128: super .meet(node);
129: SqlExpr left = node.getLeftArg();
130: SqlExpr right = node.getRightArg();
131: if (left instanceof SqlNull || right instanceof SqlNull) {
132: replace(node, new SqlNull());
133: }
134: }
135:
136: @Override
137: public void meet(SqlConcat node) throws RuntimeException {
138: super .meet(node);
139: SqlExpr left = node.getLeftArg();
140: SqlExpr right = node.getRightArg();
141: if (left instanceof StringValue && right instanceof StringValue) {
142: StringValue l = (StringValue) left;
143: StringValue r = (StringValue) right;
144: replace(node, new StringValue(l.getValue() + r.getValue()));
145: }
146: }
147:
148: @Override
149: public void meet(SqlEq node) throws RuntimeException {
150: super .meet(node);
151: SqlExpr left = node.getLeftArg();
152: SqlExpr right = node.getRightArg();
153: if (left instanceof SqlNull || right instanceof SqlNull) {
154: replace(node, new SqlNull());
155: } else if (left instanceof SqlConstant<?>
156: && right instanceof SqlConstant<?>) {
157: SqlConstant<?> l = (SqlConstant<?>) left;
158: SqlConstant<?> r = (SqlConstant<?>) right;
159: if (l.getValue().equals(r.getValue())) {
160: replace(node, new TrueValue());
161: } else {
162: replace(node, new FalseValue());
163: }
164: }
165: }
166:
167: @Override
168: public void meet(SqlIsNull node) throws RuntimeException {
169: super .meet(node);
170: SqlExpr arg = node.getArg();
171: if (arg instanceof SqlNull) {
172: replace(node, new TrueValue());
173: } else if (arg instanceof SqlConstant<?>) {
174: replace(node, new FalseValue());
175: } else if (arg instanceof SqlCase) {
176: SqlExpr rep = null;
177: SqlExpr prev = null;
178: SqlCase scase = (SqlCase) arg;
179: for (Entry entry : scase.getEntries()) {
180: SqlExpr condition = entry.getCondition();
181: if (rep == null) {
182: rep = and(condition.clone(), isNull(entry
183: .getResult().clone()));
184: prev = not(condition.clone());
185: } else {
186: rep = or(rep, and(and(prev.clone(), condition
187: .clone()),
188: isNull(entry.getResult().clone())));
189: prev = and(prev, not(condition.clone()));
190: }
191: }
192: replace(node, or(rep, prev.clone()));
193: }
194: }
195:
196: @Override
197: public void meet(SqlLowerCase node) throws RuntimeException {
198: super .meet(node);
199: if (node.getArg() instanceof SqlNull) {
200: replace(node, new SqlNull());
201: } else if (node.getArg() instanceof SqlConstant) {
202: SqlConstant arg = (SqlConstant) node.getArg();
203: String lower = arg.getValue().toString().toLowerCase(
204: Locale.US);
205: replace(node, str(lower));
206: }
207: }
208:
209: @Override
210: public void meet(SqlNot node) throws RuntimeException {
211: super .meet(node);
212: SqlExpr arg = node.getArg();
213: if (arg instanceof TrueValue) {
214: replace(node, new FalseValue());
215: } else if (arg instanceof FalseValue) {
216: replace(node, new TrueValue());
217: } else if (arg instanceof SqlNull) {
218: replace(node, new SqlNull());
219: } else if (arg instanceof SqlNot) {
220: SqlNot not = (SqlNot) arg;
221: replace(node, not.getArg().clone());
222: } else if (arg instanceof SqlOr) {
223: SqlOr or = (SqlOr) arg;
224: replace(node, and(not(or.getLeftArg().clone()), not(or
225: .getRightArg().clone())));
226: }
227: }
228:
229: @Override
230: public void meet(SqlOr node) throws RuntimeException {
231: super .meet(node);
232: SqlExpr left = node.getLeftArg();
233: SqlExpr right = node.getRightArg();
234: if (left instanceof TrueValue || right instanceof TrueValue) {
235: replace(node, new TrueValue());
236: } else if (left instanceof FalseValue
237: && right instanceof FalseValue) {
238: replace(node, new FalseValue());
239: } else if (left instanceof FalseValue) {
240: replace(node, right.clone());
241: } else if (right instanceof FalseValue) {
242: replace(node, left.clone());
243: } else if (right instanceof SqlNull && andAllTheWay(node)) {
244: replace(node, left.clone());
245: } else if (left instanceof SqlNull && andAllTheWay(node)) {
246: replace(node, right.clone());
247: } else if (right instanceof SqlNull && left instanceof SqlNull) {
248: replace(node, new SqlNull());
249: } else if (left instanceof SqlNull && right instanceof SqlOr) {
250: SqlOr r = (SqlOr) right;
251: SqlExpr rleft = r.getLeftArg();
252: SqlExpr rright = r.getRightArg();
253: if (rleft instanceof SqlNull || rright instanceof SqlNull) {
254: replace(node, right.clone());
255: }
256: } else if (right instanceof SqlNull && left instanceof SqlOr) {
257: SqlOr l = (SqlOr) left;
258: SqlExpr lleft = l.getLeftArg();
259: SqlExpr lright = l.getRightArg();
260: if (lleft instanceof SqlNull || lright instanceof SqlNull) {
261: replace(node, left.clone());
262: }
263: } else if (right instanceof SqlNull && left instanceof SqlAnd) {
264: // value IS NOT NULL AND value = ? OR NULL
265: // -> value = ?
266: SqlAnd l = (SqlAnd) left;
267: SqlExpr lleft = l.getLeftArg();
268: SqlExpr lright = l.getRightArg();
269: SqlExpr isNotNull = arg(arg(lleft, SqlNot.class),
270: SqlIsNull.class);
271: SqlExpr isNotEq = other(lright, isNotNull, SqlEq.class);
272: if (isNotEq instanceof SqlConstant) {
273: replace(node, lright);
274: }
275: }
276: }
277:
278: public void optimize(SqlExpr sqlExpr) {
279: sqlExpr.visit(this );
280: }
281:
282: public void optimize(TupleExpr tupleExpr, Dataset dataset,
283: BindingSet bindings) {
284: tupleExpr.visit(this );
285: }
286:
287: private boolean andAllTheWay(QueryModelNode node) {
288: if (node.getParentNode() instanceof SelectQuery)
289: return true;
290: if (node.getParentNode() instanceof FromItem)
291: return true;
292: if (node.getParentNode() instanceof SqlAnd)
293: return andAllTheWay(node.getParentNode());
294: return false;
295: }
296:
297: private SqlExpr arg(SqlExpr node,
298: Class<? extends UnarySqlOperator> type) {
299: if (type.isInstance(node))
300: return type.cast(node).getArg();
301: return null;
302: }
303:
304: private SqlExpr other(SqlExpr node, SqlExpr compare,
305: Class<? extends BinarySqlOperator> type) {
306: if (type.isInstance(node)) {
307: BinarySqlOperator cast = type.cast(node);
308: SqlExpr left = cast.getLeftArg();
309: SqlExpr right = cast.getRightArg();
310: if (left.equals(compare))
311: return right;
312: if (right.equals(compare))
313: return left;
314: }
315: return null;
316: }
317:
318: private void replace(SqlExpr before, SqlExpr after) {
319: before.replaceWith(after);
320: after.visit(this);
321: }
322: }
|