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.algebra.factories;
007:
008: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.abs;
009: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.and;
010: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.cmp;
011: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.concat;
012: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.eq;
013: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.eqComparingNull;
014: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.eqIfNotNull;
015: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.gt;
016: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.isNotNull;
017: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.isNull;
018: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.like;
019: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.lowercase;
020: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.neq;
021: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.not;
022: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.num;
023: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.or;
024: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.regex;
025: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.simple;
026: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.sqlNull;
027: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.str;
028: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.sub;
029: import static org.openrdf.sail.rdbms.algebra.base.SqlExprSupport.unsupported;
030:
031: import org.openrdf.model.Literal;
032: import org.openrdf.model.Value;
033: import org.openrdf.model.vocabulary.XMLSchema;
034: import org.openrdf.query.algebra.And;
035: import org.openrdf.query.algebra.Bound;
036: import org.openrdf.query.algebra.Compare;
037: import org.openrdf.query.algebra.IsBNode;
038: import org.openrdf.query.algebra.IsLiteral;
039: import org.openrdf.query.algebra.IsResource;
040: import org.openrdf.query.algebra.IsURI;
041: import org.openrdf.query.algebra.LangMatches;
042: import org.openrdf.query.algebra.Not;
043: import org.openrdf.query.algebra.Or;
044: import org.openrdf.query.algebra.QueryModelNode;
045: import org.openrdf.query.algebra.Regex;
046: import org.openrdf.query.algebra.SameTerm;
047: import org.openrdf.query.algebra.ValueConstant;
048: import org.openrdf.query.algebra.ValueExpr;
049: import org.openrdf.query.algebra.Var;
050: import org.openrdf.query.algebra.Compare.CompareOp;
051: import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
052: import org.openrdf.sail.rdbms.algebra.FalseValue;
053: import org.openrdf.sail.rdbms.algebra.RefIdColumn;
054: import org.openrdf.sail.rdbms.algebra.SqlCase;
055: import org.openrdf.sail.rdbms.algebra.SqlNull;
056: import org.openrdf.sail.rdbms.algebra.TrueValue;
057: import org.openrdf.sail.rdbms.algebra.base.SqlExpr;
058: import org.openrdf.sail.rdbms.exceptions.UnsupportedRdbmsOperatorException;
059:
060: /**
061: * Boolean SQL expression factory. This factory can convert a number of core
062: * algebra nodes into an SQL expression.
063: *
064: * @author James Leigh
065: *
066: */
067: public class BooleanExprFactory extends
068: QueryModelVisitorBase<UnsupportedRdbmsOperatorException> {
069: private static final double HR14 = 14 * 60 * 60 * 1000;
070: protected SqlExpr result;
071: private SqlExprFactory sql;
072:
073: public SqlExpr createBooleanExpr(ValueExpr expr)
074: throws UnsupportedRdbmsOperatorException {
075: result = null;
076: if (expr == null)
077: return new SqlNull();
078: expr.visit(this );
079: if (result == null)
080: return new SqlNull();
081: return result;
082: }
083:
084: @Override
085: public void meet(And node) throws UnsupportedRdbmsOperatorException {
086: result = and(bool(node.getLeftArg()), bool(node.getRightArg()));
087: }
088:
089: @Override
090: public void meet(Bound node)
091: throws UnsupportedRdbmsOperatorException {
092: result = not(isNull(new RefIdColumn(node.getArg())));
093: }
094:
095: @Override
096: public void meet(Compare compare)
097: throws UnsupportedRdbmsOperatorException {
098: ValueExpr left = compare.getLeftArg();
099: ValueExpr right = compare.getRightArg();
100: CompareOp op = compare.getOperator();
101: switch (op) {
102: case EQ:
103: if (isTerm(left) && isTerm(right)) {
104: result = termsEqual(left, right);
105: } else {
106: result = equal(left, right);
107: }
108: break;
109: case NE:
110: if (isTerm(left) && isTerm(right)) {
111: result = not(termsEqual(left, right));
112: } else {
113: result = not(equal(left, right));
114: }
115: break;
116: case GE:
117: case GT:
118: case LE:
119: case LT:
120: SqlExpr simple = and(simple(type(left)),
121: simple(type(right)));
122: SqlExpr labels = and(cmp(label(left), op, label(right)),
123: simple);
124: SqlExpr time = cmp(time(left), op, time(right));
125: SqlExpr within = cmp(time(left), op, sub(time(right),
126: num(HR14)));
127: SqlExpr comp = or(eq(zoned(left), zoned(right)), within);
128: SqlExpr dateTime = and(eq(type(left), type(right)), and(
129: comp, time));
130: result = or(cmp(numeric(left), op, numeric(right)), or(
131: dateTime, labels));
132: break;
133: }
134: }
135:
136: @Override
137: public void meet(IsBNode node)
138: throws UnsupportedRdbmsOperatorException {
139: result = isNotNull(sql.createBNodeExpr(node.getArg()));
140: }
141:
142: @Override
143: public void meet(IsLiteral node)
144: throws UnsupportedRdbmsOperatorException {
145: result = isNotNull(sql.createLabelExpr(node.getArg()));
146: }
147:
148: @Override
149: public void meet(IsResource node)
150: throws UnsupportedRdbmsOperatorException {
151: SqlExpr isBNode = isNotNull(sql.createBNodeExpr(node.getArg()));
152: result = or(isBNode,
153: isNotNull(sql.createUriExpr(node.getArg())));
154: }
155:
156: @Override
157: public void meet(IsURI node)
158: throws UnsupportedRdbmsOperatorException {
159: result = isNotNull(sql.createUriExpr(node.getArg()));
160: }
161:
162: @Override
163: public void meet(LangMatches node)
164: throws UnsupportedRdbmsOperatorException {
165: ValueExpr left = node.getLeftArg();
166: ValueExpr right = node.getRightArg();
167: SqlCase sqlCase = new SqlCase();
168: sqlCase.when(eq(label(right), str("*")), neq(label(left),
169: str("")));
170: SqlExpr pattern = concat(lowercase(label(right)), str("%"));
171: sqlCase.when(new TrueValue(), like(label(left), pattern));
172: result = sqlCase;
173: }
174:
175: @Override
176: public void meet(Not node) throws UnsupportedRdbmsOperatorException {
177: result = not(bool(node.getArg()));
178: }
179:
180: @Override
181: public void meet(Or node) throws UnsupportedRdbmsOperatorException {
182: result = or(bool(node.getLeftArg()), bool(node.getRightArg()));
183: }
184:
185: @Override
186: public void meet(Regex node)
187: throws UnsupportedRdbmsOperatorException {
188: result = regex(label(node.getArg()),
189: label(node.getPatternArg()), label(node.getFlagsArg()));
190: }
191:
192: @Override
193: public void meet(SameTerm node)
194: throws UnsupportedRdbmsOperatorException {
195: ValueExpr left = node.getLeftArg();
196: ValueExpr right = node.getRightArg();
197: boolean leftIsVar = left instanceof Var;
198: boolean rightIsVar = right instanceof Var;
199: boolean leftIsConst = left instanceof ValueConstant;
200: boolean rightIsConst = right instanceof ValueConstant;
201: if (leftIsVar && rightIsVar) {
202: result = eq(new RefIdColumn((Var) left), new RefIdColumn(
203: (Var) right));
204: } else if ((leftIsVar || leftIsConst)
205: && (rightIsVar || rightIsConst)) {
206: result = eq(hash(left), hash(right));
207: } else {
208: SqlExpr bnodes = eqComparingNull(bNode(left), bNode(right));
209: SqlExpr uris = eqComparingNull(uri(left), uri(right));
210: SqlExpr langs = eqComparingNull(lang(left), lang(right));
211: SqlExpr datatype = eqComparingNull(type(left), type(right));
212: SqlExpr labels = eqComparingNull(label(left), label(right));
213:
214: SqlExpr literals = and(langs, and(datatype, labels));
215: result = and(bnodes, and(uris, literals));
216: }
217: }
218:
219: @Override
220: public void meet(ValueConstant vc)
221: throws UnsupportedRdbmsOperatorException {
222: result = valueOf(vc.getValue());
223: }
224:
225: @Override
226: public void meet(Var var) throws UnsupportedRdbmsOperatorException {
227: if (var.getValue() == null) {
228: result = effectiveBooleanValue(var);
229: } else {
230: result = valueOf(var.getValue());
231: }
232: }
233:
234: public void setSqlExprFactory(SqlExprFactory sql) {
235: this .sql = sql;
236: }
237:
238: protected SqlExpr bNode(ValueExpr arg)
239: throws UnsupportedRdbmsOperatorException {
240: return sql.createBNodeExpr(arg);
241: }
242:
243: protected SqlExpr bool(ValueExpr arg)
244: throws UnsupportedRdbmsOperatorException {
245: return sql.createBooleanExpr(arg);
246: }
247:
248: protected SqlExpr label(ValueExpr arg)
249: throws UnsupportedRdbmsOperatorException {
250: return sql.createLabelExpr(arg);
251: }
252:
253: protected SqlExpr lang(ValueExpr arg)
254: throws UnsupportedRdbmsOperatorException {
255: return sql.createLanguageExpr(arg);
256: }
257:
258: protected SqlExpr hash(ValueExpr arg)
259: throws UnsupportedRdbmsOperatorException {
260: return sql.createHashExpr(arg);
261: }
262:
263: @Override
264: protected void meetNode(QueryModelNode arg)
265: throws UnsupportedRdbmsOperatorException {
266: if (arg instanceof ValueExpr) {
267: result = effectiveBooleanValue((ValueExpr) arg);
268: } else {
269: throw unsupported(arg);
270: }
271: }
272:
273: protected SqlExpr numeric(ValueExpr arg)
274: throws UnsupportedRdbmsOperatorException {
275: return sql.createNumericExpr(arg);
276: }
277:
278: protected SqlExpr time(ValueExpr arg)
279: throws UnsupportedRdbmsOperatorException {
280: return sql.createTimeExpr(arg);
281: }
282:
283: protected SqlExpr type(ValueExpr arg)
284: throws UnsupportedRdbmsOperatorException {
285: return sql.createDatatypeExpr(arg);
286: }
287:
288: protected SqlExpr uri(ValueExpr arg)
289: throws UnsupportedRdbmsOperatorException {
290: return sql.createUriExpr(arg);
291: }
292:
293: protected SqlExpr zoned(ValueExpr arg)
294: throws UnsupportedRdbmsOperatorException {
295: return sql.createZonedExpr(arg);
296: }
297:
298: private SqlExpr effectiveBooleanValue(ValueExpr v)
299: throws UnsupportedRdbmsOperatorException {
300: String bool = XMLSchema.BOOLEAN.stringValue();
301: SqlCase sqlCase = new SqlCase();
302: sqlCase.when(eq(type(v), str(bool)), eq(label(v), str("true")));
303: sqlCase.when(simple(type(v)), not(eq(label(v), str(""))));
304: sqlCase
305: .when(isNotNull(numeric(v)),
306: not(eq(numeric(v), num(0))));
307: return sqlCase;
308: }
309:
310: private SqlExpr equal(ValueExpr left, ValueExpr right)
311: throws UnsupportedRdbmsOperatorException {
312: SqlExpr bnodes = eq(bNode(left), bNode(right));
313: SqlExpr uris = eq(uri(left), uri(right));
314: SqlCase scase = new SqlCase();
315: scase.when(or(isNotNull(bNode(left)), isNotNull(bNode(right))),
316: bnodes);
317: scase.when(or(isNotNull(uri(left)), isNotNull(uri(right))),
318: uris);
319: return literalEqual(left, right, scase);
320: }
321:
322: private boolean isTerm(ValueExpr node) {
323: return node instanceof Var || node instanceof ValueConstant;
324: }
325:
326: private SqlExpr literalEqual(ValueExpr left, ValueExpr right,
327: SqlCase scase) throws UnsupportedRdbmsOperatorException {
328: // TODO What about xsd:booleans?
329: SqlExpr labels = eq(label(left), label(right));
330: SqlExpr langs = and(eqIfNotNull(lang(left), lang(right)),
331: labels.clone());
332: SqlExpr numeric = eq(numeric(left), numeric(right));
333: SqlExpr time = eq(time(left), time(right));
334:
335: SqlExpr bothCalendar = and(isNotNull(time(left)),
336: isNotNull(time(right)));
337: SqlExpr over14 = gt(abs(sub(time(left), time(right))),
338: num(HR14 / 2));
339: SqlExpr comparable = and(bothCalendar, or(eq(zoned(left),
340: zoned(right)), over14));
341:
342: scase.when(or(isNotNull(lang(left)), isNotNull(lang(right))),
343: langs);
344: scase.when(and(simple(type(left)), simple(type(right))), labels
345: .clone());
346: scase.when(and(isNotNull(numeric(left)),
347: isNotNull(numeric(right))), numeric);
348: scase.when(comparable, time);
349: scase.when(and(eq(type(left), type(right)), labels.clone()),
350: new TrueValue());
351: return scase;
352: }
353:
354: private SqlExpr termsEqual(ValueExpr left, ValueExpr right)
355: throws UnsupportedRdbmsOperatorException {
356: SqlExpr bnodes = eqIfNotNull(bNode(left), bNode(right));
357: SqlExpr uris = eqIfNotNull(uri(left), uri(right));
358: SqlCase scase = new SqlCase();
359: scase.when(or(isNotNull(bNode(left)), isNotNull(bNode(right))),
360: bnodes);
361: scase.when(or(isNotNull(uri(left)), isNotNull(uri(right))),
362: uris);
363: return literalEqual(left, right, scase);
364: }
365:
366: private SqlExpr valueOf(Value value) {
367: if (value instanceof Literal) {
368: if (((Literal) value).booleanValue()) {
369: return new TrueValue();
370: }
371: return new FalseValue();
372: }
373: return sqlNull();
374: }
375: }
|