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.evaluation;
007:
008: import java.util.ArrayList;
009: import java.util.List;
010:
011: import org.openrdf.model.Value;
012: import org.openrdf.sail.rdbms.RdbmsValueFactory;
013: import org.openrdf.sail.rdbms.algebra.BNodeColumn;
014: import org.openrdf.sail.rdbms.algebra.ColumnVar;
015: import org.openrdf.sail.rdbms.algebra.DatatypeColumn;
016: import org.openrdf.sail.rdbms.algebra.DateTimeColumn;
017: import org.openrdf.sail.rdbms.algebra.DoubleValue;
018: import org.openrdf.sail.rdbms.algebra.FalseValue;
019: import org.openrdf.sail.rdbms.algebra.HashColumn;
020: import org.openrdf.sail.rdbms.algebra.IdColumn;
021: import org.openrdf.sail.rdbms.algebra.JoinItem;
022: import org.openrdf.sail.rdbms.algebra.LabelColumn;
023: import org.openrdf.sail.rdbms.algebra.LanguageColumn;
024: import org.openrdf.sail.rdbms.algebra.LongLabelColumn;
025: import org.openrdf.sail.rdbms.algebra.LongURIColumn;
026: import org.openrdf.sail.rdbms.algebra.NumberValue;
027: import org.openrdf.sail.rdbms.algebra.NumericColumn;
028: import org.openrdf.sail.rdbms.algebra.RefIdColumn;
029: import org.openrdf.sail.rdbms.algebra.SqlAbs;
030: import org.openrdf.sail.rdbms.algebra.SqlAnd;
031: import org.openrdf.sail.rdbms.algebra.SqlCase;
032: import org.openrdf.sail.rdbms.algebra.SqlCompare;
033: import org.openrdf.sail.rdbms.algebra.SqlConcat;
034: import org.openrdf.sail.rdbms.algebra.SqlEq;
035: import org.openrdf.sail.rdbms.algebra.SqlIsNull;
036: import org.openrdf.sail.rdbms.algebra.SqlLike;
037: import org.openrdf.sail.rdbms.algebra.SqlLowerCase;
038: import org.openrdf.sail.rdbms.algebra.SqlMathExpr;
039: import org.openrdf.sail.rdbms.algebra.SqlNot;
040: import org.openrdf.sail.rdbms.algebra.SqlNull;
041: import org.openrdf.sail.rdbms.algebra.SqlOr;
042: import org.openrdf.sail.rdbms.algebra.SqlRegex;
043: import org.openrdf.sail.rdbms.algebra.SqlShift;
044: import org.openrdf.sail.rdbms.algebra.StringValue;
045: import org.openrdf.sail.rdbms.algebra.TrueValue;
046: import org.openrdf.sail.rdbms.algebra.URIColumn;
047: import org.openrdf.sail.rdbms.algebra.UnionItem;
048: import org.openrdf.sail.rdbms.algebra.base.BinarySqlOperator;
049: import org.openrdf.sail.rdbms.algebra.base.FromItem;
050: import org.openrdf.sail.rdbms.algebra.base.SqlConstant;
051: import org.openrdf.sail.rdbms.algebra.base.SqlExpr;
052: import org.openrdf.sail.rdbms.algebra.base.UnarySqlOperator;
053: import org.openrdf.sail.rdbms.algebra.base.ValueColumnBase;
054: import org.openrdf.sail.rdbms.exceptions.RdbmsException;
055: import org.openrdf.sail.rdbms.exceptions.UnsupportedRdbmsOperatorException;
056:
057: /**
058: * Constructs an SQL query from {@link SqlExpr}s and {@link FromItem}s.
059: *
060: * @author James Leigh
061: *
062: */
063: public class QueryBuilder {
064: private SqlQueryBuilder query;
065: private RdbmsValueFactory vf;
066: private boolean usingHashTable;
067:
068: public QueryBuilder(SqlQueryBuilder builder) {
069: super ();
070: this .query = builder;
071: }
072:
073: public void setValueFactory(RdbmsValueFactory vf) {
074: this .vf = vf;
075: }
076:
077: public void setUsingHashTable(boolean usingHashTable) {
078: this .usingHashTable = usingHashTable;
079: }
080:
081: public void distinct() {
082: query.distinct();
083: }
084:
085: public QueryBuilder filter(ColumnVar var, Value val)
086: throws RdbmsException {
087: String alias = var.getAlias();
088: String column = var.getColumn();
089: query.filter().and().columnEquals(alias, column,
090: vf.getInternalId(val));
091: return this ;
092: }
093:
094: public void from(FromItem from) throws RdbmsException,
095: UnsupportedRdbmsOperatorException {
096: from(query, from);
097: }
098:
099: public List<?> getParameters() {
100: return query.findParameters(new ArrayList<Object>());
101: }
102:
103: public void limit(Integer limit) {
104: query.limit(limit);
105: }
106:
107: public void offset(Integer offset) {
108: query.offset(offset);
109: }
110:
111: public void orderBy(SqlExpr expr, boolean isAscending)
112: throws UnsupportedRdbmsOperatorException {
113: SqlExprBuilder orderBy = query.orderBy();
114: dispatch(expr, orderBy);
115: if (!isAscending) {
116: orderBy.append(" DESC");
117: }
118: }
119:
120: public QueryBuilder select(SqlExpr expr)
121: throws UnsupportedRdbmsOperatorException {
122: dispatch(expr, query.select());
123: return this ;
124: }
125:
126: @Override
127: public String toString() {
128: return query.toString();
129: }
130:
131: private void append(BNodeColumn var, SqlExprBuilder filter) {
132: String alias = getBNodeAlias(var.getRdbmsVar());
133: filter.column(alias, "value");
134: }
135:
136: private void append(DatatypeColumn var, SqlExprBuilder filter) {
137: if (var.getRdbmsVar().isResource()) {
138: filter.appendNull();
139: } else {
140: String alias = getDatatypeAlias(var.getRdbmsVar());
141: filter.column(alias, "value");
142: }
143: }
144:
145: private void append(DateTimeColumn var, SqlExprBuilder filter) {
146: if (var.getRdbmsVar().isResource()) {
147: filter.appendNull();
148: } else {
149: String alias = getDateTimeAlias(var.getRdbmsVar());
150: filter.column(alias, "value");
151: }
152: }
153:
154: private void append(DoubleValue expr, SqlExprBuilder filter) {
155: filter.appendNumeric(expr.getValue());
156: }
157:
158: private void append(FalseValue expr, SqlExprBuilder filter) {
159: filter.appendBoolean(false);
160: }
161:
162: private void append(HashColumn var, SqlExprBuilder filter) {
163: if (usingHashTable) {
164: String alias = getHashAlias(var.getRdbmsVar());
165: filter.column(alias, "value");
166: } else {
167: filter.column(var.getAlias(), var.getColumn());
168: }
169: }
170:
171: private void append(IdColumn expr, SqlExprBuilder filter) {
172: filter.column(expr.getAlias(), expr.getColumn());
173: }
174:
175: private void append(LabelColumn var, SqlExprBuilder filter) {
176: if (var.getRdbmsVar().isResource()) {
177: filter.appendNull();
178: } else {
179: String alias = getLabelAlias(var.getRdbmsVar());
180: filter.column(alias, "value");
181: }
182: }
183:
184: private void append(LongLabelColumn var, SqlExprBuilder filter) {
185: if (var.getRdbmsVar().isResource()) {
186: filter.appendNull();
187: } else {
188: String alias = getLongLabelAlias(var.getRdbmsVar());
189: filter.column(alias, "value");
190: }
191: }
192:
193: private void append(LanguageColumn var, SqlExprBuilder filter) {
194: if (var.getRdbmsVar().isResource()) {
195: filter.appendNull();
196: } else {
197: String alias = getLanguageAlias(var.getRdbmsVar());
198: filter.column(alias, "value");
199: }
200: }
201:
202: private void append(LongURIColumn uri, SqlExprBuilder filter) {
203: ColumnVar var = uri.getRdbmsVar();
204: String alias = getLongURIAlias(var);
205: filter.column(alias, "value");
206: }
207:
208: private void append(NumberValue expr, SqlExprBuilder filter) {
209: filter.number(expr.getValue());
210: }
211:
212: private void append(NumericColumn var, SqlExprBuilder filter) {
213: if (var.getRdbmsVar().isResource()) {
214: filter.appendNull();
215: } else {
216: String alias = getNumericAlias(var.getRdbmsVar());
217: filter.column(alias, "value");
218: }
219: }
220:
221: private void append(RefIdColumn expr, SqlExprBuilder filter) {
222: filter.column(expr.getAlias(), expr.getColumn());
223: }
224:
225: private void append(SqlAbs expr, SqlExprBuilder filter)
226: throws UnsupportedRdbmsOperatorException {
227: SqlBracketBuilder abs = filter.abs();
228: dispatch(expr.getArg(), abs);
229: abs.close();
230: }
231:
232: private void append(SqlAnd expr, SqlExprBuilder filter)
233: throws UnsupportedRdbmsOperatorException {
234: dispatch(expr.getLeftArg(), filter);
235: filter.and();
236: dispatch(expr.getRightArg(), filter);
237: }
238:
239: private void append(SqlCase expr, SqlExprBuilder filter)
240: throws UnsupportedRdbmsOperatorException {
241: SqlCaseBuilder caseExpr = filter.caseBegin();
242: for (SqlCase.Entry e : expr.getEntries()) {
243: caseExpr.when();
244: dispatch(e.getCondition(), filter);
245: caseExpr.then();
246: dispatch(e.getResult(), filter);
247: }
248: caseExpr.end();
249: }
250:
251: private void append(SqlCompare expr, SqlExprBuilder filter)
252: throws UnsupportedRdbmsOperatorException {
253: dispatch(expr.getLeftArg(), filter);
254: filter.appendOperator(expr.getOperator());
255: dispatch(expr.getRightArg(), filter);
256: }
257:
258: private void append(SqlConcat expr, SqlExprBuilder filter)
259: throws UnsupportedRdbmsOperatorException {
260: SqlBracketBuilder open = filter.open();
261: dispatch(expr.getLeftArg(), open);
262: open.concat();
263: dispatch(expr.getRightArg(), open);
264: open.close();
265: }
266:
267: private void append(SqlEq expr, SqlExprBuilder filter)
268: throws UnsupportedRdbmsOperatorException {
269: dispatch(expr.getLeftArg(), filter);
270: filter.eq();
271: dispatch(expr.getRightArg(), filter);
272: }
273:
274: private void append(SqlIsNull expr, SqlExprBuilder filter)
275: throws UnsupportedRdbmsOperatorException {
276: dispatch(expr.getArg(), filter);
277: filter.isNull();
278: }
279:
280: private void append(SqlLike expr, SqlExprBuilder filter)
281: throws UnsupportedRdbmsOperatorException {
282: dispatch(expr.getLeftArg(), filter);
283: filter.like();
284: dispatch(expr.getRightArg(), filter);
285: }
286:
287: private void append(SqlLowerCase expr, SqlExprBuilder filter)
288: throws UnsupportedRdbmsOperatorException {
289: SqlBracketBuilder lower = filter.lowerCase();
290: dispatch(expr.getArg(), lower);
291: lower.close();
292: }
293:
294: private void append(SqlMathExpr expr, SqlExprBuilder filter)
295: throws UnsupportedRdbmsOperatorException {
296: dispatch(expr.getLeftArg(), filter);
297: filter.math(expr.getOperator());
298: dispatch(expr.getRightArg(), filter);
299: }
300:
301: private void append(SqlNot expr, SqlExprBuilder filter)
302: throws UnsupportedRdbmsOperatorException {
303: if (expr.getArg() instanceof SqlIsNull) {
304: SqlIsNull arg = (SqlIsNull) expr.getArg();
305: dispatch(arg.getArg(), filter);
306: filter.isNotNull();
307: } else {
308: SqlBracketBuilder open = filter.not();
309: dispatch(expr.getArg(), open);
310: open.close();
311: }
312: }
313:
314: private void append(SqlNull expr, SqlExprBuilder filter) {
315: filter.appendNull();
316: }
317:
318: private void append(SqlOr expr, SqlExprBuilder filter)
319: throws UnsupportedRdbmsOperatorException {
320: SqlBracketBuilder open = filter.open();
321: dispatch(expr.getLeftArg(), open);
322: open.or();
323: dispatch(expr.getRightArg(), open);
324: open.close();
325: }
326:
327: private void append(SqlRegex expr, SqlExprBuilder filter)
328: throws UnsupportedRdbmsOperatorException {
329: SqlRegexBuilder regex = filter.regex();
330: dispatch(expr.getArg(), regex.value());
331: dispatch(expr.getPatternArg(), regex.pattern());
332: SqlExpr flags = expr.getFlagsArg();
333: if (flags != null) {
334: dispatch(flags, regex.flags());
335: }
336: regex.close();
337: }
338:
339: private void append(SqlShift expr, SqlExprBuilder filter)
340: throws UnsupportedRdbmsOperatorException {
341: SqlBracketBuilder mod = filter.mod(expr.getRange());
342: SqlBracketBuilder open = mod.open();
343: dispatch(expr.getArg(), open);
344: open.rightShift(expr.getRightShift());
345: open.close();
346: mod.plus(expr.getRange());
347: mod.close();
348: }
349:
350: private void append(StringValue expr, SqlExprBuilder filter) {
351: filter.varchar(expr.getValue());
352: }
353:
354: private void append(TrueValue expr, SqlExprBuilder filter) {
355: filter.appendBoolean(true);
356: }
357:
358: private void append(URIColumn uri, SqlExprBuilder filter) {
359: ColumnVar var = uri.getRdbmsVar();
360: String alias = getURIAlias(var);
361: filter.column(alias, "value");
362: }
363:
364: private void dispatch(SqlExpr expr, SqlExprBuilder filter)
365: throws UnsupportedRdbmsOperatorException {
366: if (expr instanceof ValueColumnBase) {
367: dispatchValueColumnBase((ValueColumnBase) expr, filter);
368: } else if (expr instanceof IdColumn) {
369: append((IdColumn) expr, filter);
370: } else if (expr instanceof SqlConstant<?>) {
371: dispatchSqlConstant((SqlConstant<?>) expr, filter);
372: } else if (expr instanceof UnarySqlOperator) {
373: dispatchUnarySqlOperator((UnarySqlOperator) expr, filter);
374: } else if (expr instanceof BinarySqlOperator) {
375: dispatchBinarySqlOperator((BinarySqlOperator) expr, filter);
376: } else {
377: dispatchOther(expr, filter);
378: }
379: }
380:
381: private void dispatchBinarySqlOperator(BinarySqlOperator expr,
382: SqlExprBuilder filter)
383: throws UnsupportedRdbmsOperatorException {
384: if (expr instanceof SqlAnd) {
385: append((SqlAnd) expr, filter);
386: } else if (expr instanceof SqlEq) {
387: append((SqlEq) expr, filter);
388: } else if (expr instanceof SqlOr) {
389: append((SqlOr) expr, filter);
390: } else if (expr instanceof SqlCompare) {
391: append((SqlCompare) expr, filter);
392: } else if (expr instanceof SqlRegex) {
393: append((SqlRegex) expr, filter);
394: } else if (expr instanceof SqlConcat) {
395: append((SqlConcat) expr, filter);
396: } else if (expr instanceof SqlMathExpr) {
397: append((SqlMathExpr) expr, filter);
398: } else if (expr instanceof SqlLike) {
399: append((SqlLike) expr, filter);
400: } else {
401: throw unsupported(expr);
402: }
403: }
404:
405: private void dispatchOther(SqlExpr expr, SqlExprBuilder filter)
406: throws UnsupportedRdbmsOperatorException {
407: if (expr instanceof SqlCase) {
408: append((SqlCase) expr, filter);
409: } else {
410: throw unsupported(expr);
411: }
412: }
413:
414: private void dispatchSqlConstant(SqlConstant<?> expr,
415: SqlExprBuilder filter)
416: throws UnsupportedRdbmsOperatorException {
417: if (expr instanceof DoubleValue) {
418: append((DoubleValue) expr, filter);
419: } else if (expr instanceof FalseValue) {
420: append((FalseValue) expr, filter);
421: } else if (expr instanceof TrueValue) {
422: append((TrueValue) expr, filter);
423: } else if (expr instanceof NumberValue) {
424: append((NumberValue) expr, filter);
425: } else if (expr instanceof SqlNull) {
426: append((SqlNull) expr, filter);
427: } else if (expr instanceof StringValue) {
428: append((StringValue) expr, filter);
429: } else {
430: throw unsupported(expr);
431: }
432: }
433:
434: private void dispatchUnarySqlOperator(UnarySqlOperator expr,
435: SqlExprBuilder filter)
436: throws UnsupportedRdbmsOperatorException {
437: if (expr instanceof SqlAbs) {
438: append((SqlAbs) expr, filter);
439: } else if (expr instanceof SqlIsNull) {
440: append((SqlIsNull) expr, filter);
441: } else if (expr instanceof SqlNot) {
442: append((SqlNot) expr, filter);
443: } else if (expr instanceof SqlShift) {
444: append((SqlShift) expr, filter);
445: } else if (expr instanceof SqlLowerCase) {
446: append((SqlLowerCase) expr, filter);
447: } else {
448: throw unsupported(expr);
449: }
450: }
451:
452: private void dispatchValueColumnBase(ValueColumnBase expr,
453: SqlExprBuilder filter)
454: throws UnsupportedRdbmsOperatorException {
455: if (expr instanceof BNodeColumn) {
456: append((BNodeColumn) expr, filter);
457: } else if (expr instanceof DatatypeColumn) {
458: append((DatatypeColumn) expr, filter);
459: } else if (expr instanceof HashColumn) {
460: append((HashColumn) expr, filter);
461: } else if (expr instanceof DateTimeColumn) {
462: append((DateTimeColumn) expr, filter);
463: } else if (expr instanceof LabelColumn) {
464: append((LabelColumn) expr, filter);
465: } else if (expr instanceof LongLabelColumn) {
466: append((LongLabelColumn) expr, filter);
467: } else if (expr instanceof LongURIColumn) {
468: append((LongURIColumn) expr, filter);
469: } else if (expr instanceof LanguageColumn) {
470: append((LanguageColumn) expr, filter);
471: } else if (expr instanceof NumericColumn) {
472: append((NumericColumn) expr, filter);
473: } else if (expr instanceof URIColumn) {
474: append((URIColumn) expr, filter);
475: } else if (expr instanceof RefIdColumn) {
476: append((RefIdColumn) expr, filter);
477: } else {
478: throw unsupported(expr);
479: }
480: }
481:
482: private void from(SqlQueryBuilder subquery, FromItem item)
483: throws RdbmsException, UnsupportedRdbmsOperatorException {
484: assert !item.isLeft() : item;
485: String alias = item.getAlias();
486: if (item instanceof JoinItem) {
487: String tableName = ((JoinItem) item).getTableName();
488: subJoinAndFilter(subquery.from(tableName, alias), item);
489: } else {
490: subJoinAndFilter(subquery.from(alias), item);
491: }
492: }
493:
494: private String getBNodeAlias(ColumnVar var) {
495: return "b" + getDBName(var);
496: }
497:
498: private String getDatatypeAlias(ColumnVar var) {
499: return "d" + getDBName(var);
500: }
501:
502: private String getDateTimeAlias(ColumnVar var) {
503: return "t" + getDBName(var);
504: }
505:
506: private String getDBName(ColumnVar var) {
507: String name = var.getName();
508: if (name.indexOf('-') >= 0)
509: return name.replace('-', '_');
510: return "_" + name; // might be a keyword otherwise
511: }
512:
513: private String getHashAlias(ColumnVar var) {
514: return "h" + getDBName(var);
515: }
516:
517: private String getLabelAlias(ColumnVar var) {
518: return "l" + getDBName(var);
519: }
520:
521: private String getLongLabelAlias(ColumnVar var) {
522: return "ll" + getDBName(var);
523: }
524:
525: private String getLongURIAlias(ColumnVar var) {
526: return "lu" + getDBName(var);
527: }
528:
529: private String getLanguageAlias(ColumnVar var) {
530: return "g" + getDBName(var);
531: }
532:
533: private String getNumericAlias(ColumnVar var) {
534: return "n" + getDBName(var);
535: }
536:
537: private String getURIAlias(ColumnVar var) {
538: return "u" + getDBName(var);
539: }
540:
541: private void join(SqlJoinBuilder query, FromItem join)
542: throws RdbmsException, UnsupportedRdbmsOperatorException {
543: String alias = join.getAlias();
544: if (join instanceof JoinItem) {
545: String tableName = ((JoinItem) join).getTableName();
546: if (join.isLeft()) {
547: subJoinAndFilter(query.leftjoin(tableName, alias), join);
548: } else {
549: subJoinAndFilter(query.join(tableName, alias), join);
550: }
551: } else {
552: if (join.isLeft()) {
553: subJoinAndFilter(query.leftjoin(alias), join);
554: } else {
555: subJoinAndFilter(query.join(alias), join);
556: }
557: }
558: }
559:
560: private SqlJoinBuilder subJoinAndFilter(SqlJoinBuilder query,
561: FromItem from) throws RdbmsException,
562: UnsupportedRdbmsOperatorException {
563: if (from instanceof UnionItem) {
564: UnionItem union = (UnionItem) from;
565: List<String> names = union.getSelectVarNames();
566: List<ColumnVar> vars = union
567: .appendVars(new ArrayList<ColumnVar>());
568: SqlQueryBuilder subquery = query.subquery();
569: for (FromItem item : union.getUnion()) {
570: for (int i = 0, n = names.size(); i < n; i++) {
571: ColumnVar var = item.getVar(names.get(i));
572: SqlExprBuilder select = subquery.select();
573: if (var == null) {
574: select.appendNull();
575: } else {
576: select.column(var.getAlias(), var.getColumn());
577: }
578: select.as(vars.get(i).getColumn());
579: }
580: from(subquery, item);
581: subquery = subquery.union();
582: }
583: }
584: for (FromItem join : from.getJoins()) {
585: join(query, join);
586: }
587: for (SqlExpr expr : from.getFilters()) {
588: dispatch(expr, query.on().and());
589: }
590: return query;
591: }
592:
593: private UnsupportedRdbmsOperatorException unsupported(Object object)
594: throws UnsupportedRdbmsOperatorException {
595: return new UnsupportedRdbmsOperatorException(object.toString());
596: }
597:
598: }
|