/* * Copyright Aduna (http://www.aduna-software.com/) (c) 2008. * * Licensed under the Aduna BSD-style license. */ package org.openrdf.sail.rdbms.evaluation; import java.util.ArrayList; import java.util.List; import org.openrdf.model.Value; import org.openrdf.sail.rdbms.RdbmsValueFactory; import org.openrdf.sail.rdbms.algebra.BNodeColumn; import org.openrdf.sail.rdbms.algebra.ColumnVar; import org.openrdf.sail.rdbms.algebra.DatatypeColumn; import org.openrdf.sail.rdbms.algebra.DateTimeColumn; import org.openrdf.sail.rdbms.algebra.DoubleValue; import org.openrdf.sail.rdbms.algebra.FalseValue; import org.openrdf.sail.rdbms.algebra.HashColumn; import org.openrdf.sail.rdbms.algebra.IdColumn; import org.openrdf.sail.rdbms.algebra.JoinItem; import org.openrdf.sail.rdbms.algebra.LabelColumn; import org.openrdf.sail.rdbms.algebra.LanguageColumn; import org.openrdf.sail.rdbms.algebra.LongLabelColumn; import org.openrdf.sail.rdbms.algebra.LongURIColumn; import org.openrdf.sail.rdbms.algebra.NumberValue; import org.openrdf.sail.rdbms.algebra.NumericColumn; import org.openrdf.sail.rdbms.algebra.PointColumn; import org.openrdf.sail.rdbms.algebra.RefIdColumn; import org.openrdf.sail.rdbms.algebra.SqlAbs; import org.openrdf.sail.rdbms.algebra.SqlAnd; import org.openrdf.sail.rdbms.algebra.SqlCase; import org.openrdf.sail.rdbms.algebra.SqlCompare; import org.openrdf.sail.rdbms.algebra.SqlConcat; import org.openrdf.sail.rdbms.algebra.SqlEq; import org.openrdf.sail.rdbms.algebra.SqlIsNull; import org.openrdf.sail.rdbms.algebra.SqlLike; import org.openrdf.sail.rdbms.algebra.SqlLowerCase; import org.openrdf.sail.rdbms.algebra.SqlMathExpr; import org.openrdf.sail.rdbms.algebra.SqlMbrContains; import org.openrdf.sail.rdbms.algebra.SqlNot; import org.openrdf.sail.rdbms.algebra.SqlNull; import org.openrdf.sail.rdbms.algebra.SqlOr; import org.openrdf.sail.rdbms.algebra.SqlRegex; import org.openrdf.sail.rdbms.algebra.SqlShift; import org.openrdf.sail.rdbms.algebra.StringValue; import org.openrdf.sail.rdbms.algebra.TrueValue; import org.openrdf.sail.rdbms.algebra.URIColumn; import org.openrdf.sail.rdbms.algebra.UnionItem; import org.openrdf.sail.rdbms.algebra.base.BinarySqlOperator; import org.openrdf.sail.rdbms.algebra.base.FromItem; import org.openrdf.sail.rdbms.algebra.base.SqlConstant; import org.openrdf.sail.rdbms.algebra.base.SqlExpr; import org.openrdf.sail.rdbms.algebra.base.UnarySqlOperator; import org.openrdf.sail.rdbms.algebra.base.ValueColumnBase; import org.openrdf.sail.rdbms.exceptions.RdbmsException; import org.openrdf.sail.rdbms.exceptions.UnsupportedRdbmsOperatorException; /** * Constructs an SQL query from {@link SqlExpr}s and {@link FromItem}s. * * @author James Leigh * */ public class QueryBuilder { private SqlQueryBuilder query; private RdbmsValueFactory vf; private boolean usingHashTable; public QueryBuilder(SqlQueryBuilder builder) { super(); this.query = builder; } public void setValueFactory(RdbmsValueFactory vf) { this.vf = vf; } public void setUsingHashTable(boolean usingHashTable) { this.usingHashTable = usingHashTable; } public void distinct() { query.distinct(); } public QueryBuilder filter(ColumnVar var, Value val) throws RdbmsException { String alias = var.getAlias(); String column = var.getColumn(); query.filter().and().columnEquals(alias, column, vf.getInternalId(val)); return this; } public void from(FromItem from) throws RdbmsException, UnsupportedRdbmsOperatorException { from(query, from); } public List<?> getParameters() { return query.findParameters(new ArrayList<Object>()); } public void limit(Integer limit) { query.limit(limit); } public void offset(Integer offset) { query.offset(offset); } public void orderBy(SqlExpr expr, boolean isAscending) throws UnsupportedRdbmsOperatorException { SqlExprBuilder orderBy = query.orderBy(); dispatch(expr, orderBy); if (!isAscending) { orderBy.append(" DESC"); } } public QueryBuilder select(SqlExpr expr) throws UnsupportedRdbmsOperatorException { dispatch(expr, query.select()); return this; } @Override public String toString() { return query.toString(); } private void append(BNodeColumn var, SqlExprBuilder filter) { String alias = getBNodeAlias(var.getRdbmsVar()); filter.column(alias, "value"); } private void append(DatatypeColumn var, SqlExprBuilder filter) { if (var.getRdbmsVar().isResource()) { filter.appendNull(); } else { String alias = getDatatypeAlias(var.getRdbmsVar()); filter.column(alias, "value"); } } private void append(DateTimeColumn var, SqlExprBuilder filter) { if (var.getRdbmsVar().isResource()) { filter.appendNull(); } else { String alias = getDateTimeAlias(var.getRdbmsVar()); filter.column(alias, "value"); } } private void append(DoubleValue expr, SqlExprBuilder filter) { filter.appendNumeric(expr.getValue()); } private void append(FalseValue expr, SqlExprBuilder filter) { filter.appendBoolean(false); } private void append(HashColumn var, SqlExprBuilder filter) { if (usingHashTable) { String alias = getHashAlias(var.getRdbmsVar()); filter.column(alias, "value"); } else { filter.column(var.getAlias(), var.getColumn()); } } private void append(IdColumn expr, SqlExprBuilder filter) { filter.column(expr.getAlias(), expr.getColumn()); } private void append(LabelColumn var, SqlExprBuilder filter) { if (var.getRdbmsVar().isResource()) { filter.appendNull(); } else { String alias = getLabelAlias(var.getRdbmsVar()); filter.column(alias, "value"); } } private void append(LongLabelColumn var, SqlExprBuilder filter) { if (var.getRdbmsVar().isResource()) { filter.appendNull(); } else { String alias = getLongLabelAlias(var.getRdbmsVar()); filter.column(alias, "value"); } } private void append(LanguageColumn var, SqlExprBuilder filter) { if (var.getRdbmsVar().isResource()) { filter.appendNull(); } else { String alias = getLanguageAlias(var.getRdbmsVar()); filter.column(alias, "value"); } } private void append(LongURIColumn uri, SqlExprBuilder filter) { ColumnVar var = uri.getRdbmsVar(); String alias = getLongURIAlias(var); filter.column(alias, "value"); } private void append(NumberValue expr, SqlExprBuilder filter) { filter.number(expr.getValue()); } private void append(NumericColumn var, SqlExprBuilder filter) { if (var.getRdbmsVar().isResource()) { filter.appendNull(); } else { String alias = getNumericAlias(var.getRdbmsVar()); filter.column(alias, "value"); } } private void append(PointColumn var, SqlExprBuilder filter) { if (var.getRdbmsVar().isResource()) { filter.appendNull(); } else { String alias = getPointAlias(var.getRdbmsVar()); filter.column(alias, "value"); } } private void append(RefIdColumn expr, SqlExprBuilder filter) { filter.column(expr.getAlias(), expr.getColumn()); } private void append(SqlAbs expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { SqlBracketBuilder abs = filter.abs(); dispatch(expr.getArg(), abs); abs.close(); } private void append(SqlAnd expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { dispatch(expr.getLeftArg(), filter); filter.and(); dispatch(expr.getRightArg(), filter); } private void append(SqlCase expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { SqlCaseBuilder caseExpr = filter.caseBegin(); for (SqlCase.Entry e : expr.getEntries()) { caseExpr.when(); dispatch(e.getCondition(), filter); caseExpr.then(); dispatch(e.getResult(), filter); } caseExpr.end(); } private void append(SqlCompare expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { dispatch(expr.getLeftArg(), filter); filter.appendOperator(expr.getOperator()); dispatch(expr.getRightArg(), filter); } private void append(SqlConcat expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { SqlBracketBuilder open = filter.open(); dispatch(expr.getLeftArg(), open); open.concat(); dispatch(expr.getRightArg(), open); open.close(); } private void append(SqlEq expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { dispatch(expr.getLeftArg(), filter); filter.eq(); dispatch(expr.getRightArg(), filter); } private void append(SqlIsNull expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { dispatch(expr.getArg(), filter); filter.isNull(); } private void append(SqlLike expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { dispatch(expr.getLeftArg(), filter); filter.like(); dispatch(expr.getRightArg(), filter); } private void append(SqlLowerCase expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { SqlBracketBuilder lower = filter.lowerCase(); dispatch(expr.getArg(), lower); lower.close(); } private void append(SqlMathExpr expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { dispatch(expr.getLeftArg(), filter); filter.math(expr.getOperator()); dispatch(expr.getRightArg(), filter); } private void append(SqlMbrContains expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { SqlBracketBuilder openMbrContains = filter.MbrContains(); SqlBracketBuilder openPolyFromText = openMbrContains.PolyFromText(); dispatch(expr.toPolygon(), openPolyFromText); openPolyFromText.close(); openMbrContains.comma(); dispatch(expr.getPointCol(), openMbrContains); openMbrContains.close(); //MBRContains(PolyFromText('POLYGON((sw_long sw_lat, ne_long sw_lat, ne_long ne_lat, sw_long ne_lat, sw_long sw_lat))'),point) } private void append(SqlNot expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { if (expr.getArg() instanceof SqlIsNull) { SqlIsNull arg = (SqlIsNull) expr.getArg(); dispatch(arg.getArg(), filter); filter.isNotNull(); } else { SqlBracketBuilder open = filter.not(); dispatch(expr.getArg(), open); open.close(); } } private void append(SqlNull expr, SqlExprBuilder filter) { filter.appendNull(); } private void append(SqlOr expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { SqlBracketBuilder open = filter.open(); dispatch(expr.getLeftArg(), open); open.or(); dispatch(expr.getRightArg(), open); open.close(); } private void append(SqlRegex expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { SqlRegexBuilder regex = filter.regex(); dispatch(expr.getArg(), regex.value()); dispatch(expr.getPatternArg(), regex.pattern()); SqlExpr flags = expr.getFlagsArg(); if (flags != null) { dispatch(flags, regex.flags()); } regex.close(); } private void append(SqlShift expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { SqlBracketBuilder mod = filter.mod(expr.getRange()); SqlBracketBuilder open = mod.open(); dispatch(expr.getArg(), open); open.rightShift(expr.getRightShift()); open.close(); mod.plus(expr.getRange()); mod.close(); } private void append(StringValue expr, SqlExprBuilder filter) { filter.varchar(expr.getValue()); } private void append(TrueValue expr, SqlExprBuilder filter) { filter.appendBoolean(true); } private void append(URIColumn uri, SqlExprBuilder filter) { ColumnVar var = uri.getRdbmsVar(); String alias = getURIAlias(var); filter.column(alias, "value"); } private void dispatch(SqlExpr expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { if (expr instanceof ValueColumnBase) { dispatchValueColumnBase((ValueColumnBase) expr, filter); } else if (expr instanceof IdColumn) { append((IdColumn) expr, filter); } else if (expr instanceof SqlConstant<?>) { dispatchSqlConstant((SqlConstant<?>) expr, filter); } else if (expr instanceof UnarySqlOperator) { dispatchUnarySqlOperator((UnarySqlOperator) expr, filter); } else if (expr instanceof BinarySqlOperator) { dispatchBinarySqlOperator((BinarySqlOperator) expr, filter); } else { dispatchOther(expr, filter); } } private void dispatchBinarySqlOperator(BinarySqlOperator expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { if (expr instanceof SqlAnd) { append((SqlAnd) expr, filter); } else if (expr instanceof SqlEq) { append((SqlEq) expr, filter); } else if (expr instanceof SqlOr) { append((SqlOr) expr, filter); } else if (expr instanceof SqlCompare) { append((SqlCompare) expr, filter); } else if (expr instanceof SqlRegex) { append((SqlRegex) expr, filter); } else if (expr instanceof SqlConcat) { append((SqlConcat) expr, filter); } else if (expr instanceof SqlMathExpr) { append((SqlMathExpr) expr, filter); } else if (expr instanceof SqlLike) { append((SqlLike) expr, filter); } else { throw unsupported(expr); } } private void dispatchOther(SqlExpr expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { if (expr instanceof SqlCase) { append((SqlCase) expr, filter); } else if (expr instanceof SqlMbrContains) { append((SqlMbrContains) expr, filter); } else { throw unsupported(expr); } } private void dispatchSqlConstant(SqlConstant<?> expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { if (expr instanceof DoubleValue) { append((DoubleValue) expr, filter); } else if (expr instanceof FalseValue) { append((FalseValue) expr, filter); } else if (expr instanceof TrueValue) { append((TrueValue) expr, filter); } else if (expr instanceof NumberValue) { append((NumberValue) expr, filter); } else if (expr instanceof SqlNull) { append((SqlNull) expr, filter); } else if (expr instanceof StringValue) { append((StringValue) expr, filter); } else { throw unsupported(expr); } } private void dispatchUnarySqlOperator(UnarySqlOperator expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { if (expr instanceof SqlAbs) { append((SqlAbs) expr, filter); } else if (expr instanceof SqlIsNull) { append((SqlIsNull) expr, filter); } else if (expr instanceof SqlNot) { append((SqlNot) expr, filter); } else if (expr instanceof SqlShift) { append((SqlShift) expr, filter); } else if (expr instanceof SqlLowerCase) { append((SqlLowerCase) expr, filter); } else { throw unsupported(expr); } } private void dispatchValueColumnBase(ValueColumnBase expr, SqlExprBuilder filter) throws UnsupportedRdbmsOperatorException { if (expr instanceof BNodeColumn) { append((BNodeColumn) expr, filter); } else if (expr instanceof DatatypeColumn) { append((DatatypeColumn) expr, filter); } else if (expr instanceof HashColumn) { append((HashColumn) expr, filter); } else if (expr instanceof DateTimeColumn) { append((DateTimeColumn) expr, filter); } else if (expr instanceof LabelColumn) { append((LabelColumn) expr, filter); } else if (expr instanceof LongLabelColumn) { append((LongLabelColumn) expr, filter); } else if (expr instanceof LongURIColumn) { append((LongURIColumn) expr, filter); } else if (expr instanceof LanguageColumn) { append((LanguageColumn) expr, filter); } else if (expr instanceof NumericColumn) { append((NumericColumn) expr, filter); } else if (expr instanceof PointColumn) { append((PointColumn) expr, filter); } else if (expr instanceof URIColumn) { append((URIColumn) expr, filter); } else if (expr instanceof RefIdColumn) { append((RefIdColumn) expr, filter); } else { throw unsupported(expr); } } private void from(SqlQueryBuilder subquery, FromItem item) throws RdbmsException, UnsupportedRdbmsOperatorException { assert !item.isLeft() : item; String alias = item.getAlias(); if (item instanceof JoinItem) { String tableName = ((JoinItem) item).getTableName(); subJoinAndFilter(subquery.from(tableName, alias), item); } else { subJoinAndFilter(subquery.from(alias), item); } } private String getBNodeAlias(ColumnVar var) { return "b" + getDBName(var); } private String getDatatypeAlias(ColumnVar var) { return "d" + getDBName(var); } private String getDateTimeAlias(ColumnVar var) { return "t" + getDBName(var); } private String getDBName(ColumnVar var) { String name = var.getName(); if (name.indexOf('-') >= 0) return name.replace('-', '_'); return "_" + name; // might be a keyword otherwise } private String getHashAlias(ColumnVar var) { return "h" + getDBName(var); } private String getLabelAlias(ColumnVar var) { return "l" + getDBName(var); } private String getLongLabelAlias(ColumnVar var) { return "ll" + getDBName(var); } private String getLongURIAlias(ColumnVar var) { return "lu" + getDBName(var); } private String getLanguageAlias(ColumnVar var) { return "g" + getDBName(var); } private String getNumericAlias(ColumnVar var) { return "n" + getDBName(var); } private String getPointAlias(ColumnVar var) { return "p" + getDBName(var); } private String getURIAlias(ColumnVar var) { return "u" + getDBName(var); } private void join(SqlJoinBuilder query, FromItem join) throws RdbmsException, UnsupportedRdbmsOperatorException { String alias = join.getAlias(); if (join instanceof JoinItem) { String tableName = ((JoinItem) join).getTableName(); if (join.isLeft()) { subJoinAndFilter(query.leftjoin(tableName, alias), join); } else { subJoinAndFilter(query.join(tableName, alias), join); } } else { if (join.isLeft()) { subJoinAndFilter(query.leftjoin(alias), join); } else { subJoinAndFilter(query.join(alias), join); } } } private SqlJoinBuilder subJoinAndFilter(SqlJoinBuilder query, FromItem from) throws RdbmsException, UnsupportedRdbmsOperatorException { if (from instanceof UnionItem) { UnionItem union = (UnionItem) from; List<String> names = union.getSelectVarNames(); List<ColumnVar> vars = union.appendVars(new ArrayList<ColumnVar>()); SqlQueryBuilder subquery = query.subquery(); for (FromItem item : union.getUnion()) { for (int i = 0, n = names.size(); i < n; i++) { ColumnVar var = item.getVar(names.get(i)); SqlExprBuilder select = subquery.select(); if (var == null) { select.appendNull(); } else { select.column(var.getAlias(), var.getColumn()); } select.as(vars.get(i).getColumn()); } from(subquery, item); subquery = subquery.union(); } } for (FromItem join : from.getJoins()) { join(query, join); } for (SqlExpr expr : from.getFilters()) { dispatch(expr, query.on().and()); } return query; } private UnsupportedRdbmsOperatorException unsupported(Object object) throws UnsupportedRdbmsOperatorException { return new UnsupportedRdbmsOperatorException(object.toString()); } }