first working UNION (MARMOTTA-538)
Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/4a33f414 Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/4a33f414 Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/4a33f414 Branch: refs/heads/ldp Commit: 4a33f414dc1e3663bbd1cf02f6e59fa41eb15e09 Parents: 25568a1 Author: Sebastian Schaffert <[email protected]> Authored: Wed Sep 17 18:16:42 2014 +0200 Committer: Sebastian Schaffert <[email protected]> Committed: Wed Sep 17 18:16:42 2014 +0200 ---------------------------------------------------------------------- .../kiwi/sparql/builder/PatternCollector.java | 30 +++- .../sparql/builder/SQLAbstractSubquery.java | 53 ++++++ .../kiwi/sparql/builder/SQLBuilder.java | 157 +++++++++++------ .../marmotta/kiwi/sparql/builder/SQLClause.java | 80 +++++++++ .../kiwi/sparql/builder/SQLFragment.java | 27 +-- .../kiwi/sparql/builder/SQLPattern.java | 47 ++--- .../sparql/builder/SQLProjectionFinder.java | 10 +- .../kiwi/sparql/builder/SQLSubQuery.java | 91 ++++++++++ .../marmotta/kiwi/sparql/builder/SQLUnion.java | 173 +++++++++++++++++++ .../kiwi/sparql/builder/SQLVariable.java | 50 +++++- .../evaluation/KiWiEvaluationStrategyImpl.java | 30 ++++ .../exception/UnsatisfiableQueryException.java | 2 +- .../persistence/KiWiSparqlConnection.java | 12 +- .../marmotta/kiwi/sparql/test/query26.sparql | 4 +- 14 files changed, 644 insertions(+), 122 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a33f414/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/PatternCollector.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/PatternCollector.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/PatternCollector.java index 3dba067..bb8f254 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/PatternCollector.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/PatternCollector.java @@ -17,10 +17,10 @@ package org.apache.marmotta.kiwi.sparql.builder; -import org.openrdf.query.algebra.Filter; -import org.openrdf.query.algebra.LeftJoin; -import org.openrdf.query.algebra.StatementPattern; -import org.openrdf.query.algebra.TupleExpr; +import org.apache.marmotta.kiwi.persistence.KiWiDialect; +import org.openrdf.query.BindingSet; +import org.openrdf.query.Dataset; +import org.openrdf.query.algebra.*; import org.openrdf.query.algebra.helpers.QueryModelVisitorBase; import java.util.LinkedList; @@ -36,7 +36,19 @@ public class PatternCollector extends QueryModelVisitorBase<RuntimeException> { int counter = 0; - public PatternCollector(TupleExpr expr) { + + private BindingSet bindings; + private Dataset dataset; + private ValueConverter converter; + private KiWiDialect dialect; + + + public PatternCollector(TupleExpr expr, BindingSet bindings, Dataset dataset, ValueConverter converter, KiWiDialect dialect) { + this.bindings = bindings; + this.dataset = dataset; + this.converter = converter; + this.dialect = dialect; + parts.push(new SQLFragment()); expr.visit(this); } @@ -66,4 +78,12 @@ public class PatternCollector extends QueryModelVisitorBase<RuntimeException> { super.meet(node); } + + @Override + public void meet(Union node) throws RuntimeException { + // unions are treated as subqueries, don't continue collection, but add the Union to the last part + + parts.getLast().getSubqueries().add(new SQLUnion("U" + (++counter),node, bindings, dataset, converter, dialect)); + } + } http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a33f414/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLAbstractSubquery.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLAbstractSubquery.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLAbstractSubquery.java new file mode 100644 index 0000000..e0c6e49 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLAbstractSubquery.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.marmotta.kiwi.sparql.builder; + +import org.openrdf.query.algebra.ValueExpr; + +import java.util.Set; + +/** + * Common fields and methods for all subqueries. + * + * @author Sebastian Schaffert ([email protected]) + */ +public abstract class SQLAbstractSubquery extends SQLClause { + + protected String alias; + + public SQLAbstractSubquery(String alias) { + this.alias = alias; + } + + public String getAlias() { + return alias; + } + + /** + * Return the SQL variables used by the subquery; we need this to do proper mapping in the parent query. + * @return + */ + public abstract Set<SQLVariable> getQueryVariables(); + + /** + * Return the projection type of an expression in this subquery. Needed for propagation up to the parent. + * @param expr + * @return + */ + protected abstract ProjectionType getProjectionType(ValueExpr expr); +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a33f414/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java index f2dc767..804fda9 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java @@ -145,8 +145,8 @@ public class SQLBuilder { * and what internal aliases to use. */ private Map<String,SQLVariable> variables = new HashMap<>(); - private void addVariable(SQLVariable v) { - variables.put(v.getSparqlVariable().getName(),v); + protected void addVariable(SQLVariable v) { + variables.put(v.getSparqlName(),v); } @@ -204,11 +204,11 @@ public class SQLBuilder { } private void prepareBuilder() throws UnsatisfiableQueryException { - Preconditions.checkArgument(query instanceof Extension || query instanceof Order || query instanceof Group || query instanceof LeftJoin ||query instanceof Join || query instanceof Filter || query instanceof StatementPattern || query instanceof Distinct || query instanceof Slice || query instanceof Reduced); + Preconditions.checkArgument(query instanceof Union || query instanceof Extension || query instanceof Order || query instanceof Group || query instanceof LeftJoin ||query instanceof Join || query instanceof Filter || query instanceof StatementPattern || query instanceof Distinct || query instanceof Slice || query instanceof Reduced); // collect all patterns in a list, using depth-first search over the join - PatternCollector pc = new PatternCollector(query); + PatternCollector pc = new PatternCollector(query, bindings, dataset, converter, dialect); fragments = pc.parts; @@ -243,11 +243,11 @@ public class SQLBuilder { Var v = fields[i]; SQLVariable sv = variables.get(v.getName()); - if(!variables.containsKey(v.getName())) { - sv = new SQLVariable("V" + (++variableCount), v); + if(sv == null) { + sv = new SQLVariable("V" + (++variableCount), v.getName()); // select those variables that are really projected and not only needed in a grouping construct - if(new SQLProjectionFinder(query,v).found) { + if(new SQLProjectionFinder(query,v.getName()).found) { sv.setProjectionType(ProjectionType.NODE); } @@ -257,7 +257,7 @@ public class SQLBuilder { String pName = p.getName(); String vName = sv.getName(); - if (hasNodeCondition(fields[i], query)) { + if (hasNodeCondition(v.getName(), query)) { sv.getAliases().add(pName + "_" + positions[i] + "_" + vName); } @@ -270,6 +270,39 @@ public class SQLBuilder { } } } + + // subqueries: look up which variables are bound in the subqueries and add proper aliases + for(SQLAbstractSubquery sq : f.getSubqueries()) { + for(SQLVariable sq_v : sq.getQueryVariables()) { + SQLVariable sv = variables.get(sq_v.getSparqlName()); + + if(sv == null) { + sv = new SQLVariable("V" + (++variableCount), sq_v.getSparqlName()); + + // select those variables that are really projected and not only needed in a grouping construct + if(new SQLProjectionFinder(query,sq_v.getSparqlName()).found) { + sv.setProjectionType(ProjectionType.NODE); // TODO: might need other types as well in case a value is created in children + } + + addVariable(sv); + } + + String sqName = sq.getAlias(); + String vName = sv.getName(); + + if (hasNodeCondition(sq_v.getSparqlName(), query)) { + sv.getAliases().add(sqName + "_" + vName); + } + + // if the variable has been used before, add a join condition to the first occurrence + if(sv.getExpressions().size() > 0) { + sq.getConditions().add(sv.getExpressions().get(0) + " = " + sqName + "." + sq_v.getName()); + } + + sv.getExpressions().add(sqName + "." + sq_v.getName()); + + } + } } @@ -280,17 +313,17 @@ public class SQLBuilder { SQLVariable sv = variables.get(v.getName()); if(!variables.containsKey(v.getName())) { - sv = new SQLVariable("V" + (++variableCount), v); + sv = new SQLVariable("V" + (++variableCount), v.getName()); // select those variables that are really projected and not only needed in a grouping construct - if(new SQLProjectionFinder(query,v).found) { + if(new SQLProjectionFinder(query,v.getName()).found) { sv.setProjectionType(getProjectionType(ext.getExpr())); } addVariable(sv); } - if (hasNodeCondition(v, query)) { + if (hasNodeCondition(v.getName(), query)) { sv.getAliases().add(evaluateExpression(ext.getExpr(), OPTypes.ANY)); } sv.getExpressions().add(evaluateExpression(ext.getExpr(), OPTypes.ANY)); @@ -460,19 +493,31 @@ public class SQLBuilder { for (SQLPattern p : f.getPatterns()) { for(Map.Entry<SQLPattern.TripleColumns, Var> fieldEntry : p.getTripleFields().entrySet()) { - if(fieldEntry.getValue() != null && !fieldEntry.getValue().hasValue() && hasNodeCondition(fieldEntry.getValue(),query)) { + if(fieldEntry.getValue() != null && !fieldEntry.getValue().hasValue() && hasNodeCondition(fieldEntry.getValue().getName(),query)) { p.setJoinField(fieldEntry.getKey(), variables.get(fieldEntry.getValue().getName()).getName()); } } } - } + for(SQLAbstractSubquery sq : f.getSubqueries()) { + for(SQLVariable sq_v : sq.getQueryVariables()) { + if(hasNodeCondition(sq_v.getSparqlName(),query)) { + // TODO: this is needed in case we need to JOIN with the NODES table to retrieve values + } + } + } + } } private String buildSelectClause() { List<String> projections = new ArrayList<>(); - for(SQLVariable v : variables.values()) { + + // enforce order in SELECT part, we need this for merging UNION subqueries + List<SQLVariable> vars = new ArrayList<>(variables.values()); + Collections.sort(vars, SQLVariable.sparqlNameComparator); + + for(SQLVariable v : vars) { if(v.getProjectionType() != ProjectionType.NONE) { String projectedName = v.getName(); String fromName = v.getExpressions().get(0); @@ -515,7 +560,7 @@ public class SQLBuilder { } - private String buildWhereClause() throws UnsatisfiableQueryException { + private String buildWhereClause() { // build the where clause as follows: // 1. iterate over all patterns and for each resource and literal field in subject, // property, object, or context, and set a query condition according to the @@ -547,7 +592,7 @@ public class SQLBuilder { if(binding instanceof KiWiNode) { whereConditions.add(vName+" = "+((KiWiNode)binding).getId()); } else { - throw new UnsatisfiableQueryException("the values in this binding have not been created by the KiWi value factory"); + throw new IllegalStateException("the values in this binding have not been created by the KiWi value factory"); } } @@ -557,10 +602,13 @@ public class SQLBuilder { // construct the where clause StringBuilder whereClause = new StringBuilder(); for(Iterator<String> it = whereConditions.iterator(); it.hasNext(); ) { - whereClause.append(it.next()); - whereClause.append("\n "); - if(it.hasNext()) { - whereClause.append("AND "); + String condition = it.next(); + if(condition.length() > 0) { + whereClause.append(condition); + whereClause.append("\n "); + if (it.hasNext()) { + whereClause.append("AND "); + } } } return whereClause.toString(); @@ -569,7 +617,6 @@ public class SQLBuilder { private String buildOrderClause() { StringBuilder orderClause = new StringBuilder(); if(orderby.size() > 0) { - orderClause.append("ORDER BY "); for(Iterator<OrderElem> it = orderby.iterator(); it.hasNext(); ) { OrderElem elem = it.next(); orderClause.append(evaluateExpression(elem.getExpr(), OPTypes.VALUE)); @@ -591,7 +638,6 @@ public class SQLBuilder { private String buildGroupClause() { StringBuilder groupClause = new StringBuilder(); if(groupLabels.size() > 0) { - groupClause.append("GROUP BY "); for(Iterator<String> it = groupLabels.iterator(); it.hasNext(); ) { SQLVariable sv = variables.get(it.next()); @@ -876,60 +922,60 @@ public class SQLBuilder { /** * Check if a variable selecting a node actually has any attached condition; if not return false. This is used to * decide whether joining with the node itself is necessary. - * @param v + * @param varName * @param expr * @return */ - private boolean hasNodeCondition(Var v, TupleExpr expr) { + private boolean hasNodeCondition(String varName, TupleExpr expr) { if(expr instanceof Filter) { - return hasNodeCondition(v, ((UnaryTupleOperator) expr).getArg()) || hasNodeCondition(v, ((Filter) expr).getCondition()); + return hasNodeCondition(varName, ((UnaryTupleOperator) expr).getArg()) || hasNodeCondition(varName, ((Filter) expr).getCondition()); } else if(expr instanceof Extension) { for(ExtensionElem elem : ((Extension) expr).getElements()) { - if(hasNodeCondition(v, elem.getExpr())) { + if(hasNodeCondition(varName, elem.getExpr())) { return true; } } - return hasNodeCondition(v,((Extension) expr).getArg()); + return hasNodeCondition(varName,((Extension) expr).getArg()); } else if(expr instanceof Order) { for(OrderElem elem : ((Order) expr).getElements()) { - if(hasNodeCondition(v, elem.getExpr())) { + if(hasNodeCondition(varName, elem.getExpr())) { return true; } } - return hasNodeCondition(v,((Order) expr).getArg()); + return hasNodeCondition(varName,((Order) expr).getArg()); } else if(expr instanceof Group) { for(GroupElem elem : ((Group) expr).getGroupElements()) { - if(hasNodeCondition(v, elem.getOperator())) { + if(hasNodeCondition(varName, elem.getOperator())) { return true; } } - return hasNodeCondition(v,((Group) expr).getArg()); + return hasNodeCondition(varName,((Group) expr).getArg()); } else if(expr instanceof UnaryTupleOperator) { - return hasNodeCondition(v, ((UnaryTupleOperator) expr).getArg()); + return hasNodeCondition(varName, ((UnaryTupleOperator) expr).getArg()); } else if(expr instanceof BinaryTupleOperator) { - return hasNodeCondition(v, ((BinaryTupleOperator) expr).getLeftArg()) || hasNodeCondition(v, ((BinaryTupleOperator) expr).getRightArg()); + return hasNodeCondition(varName, ((BinaryTupleOperator) expr).getLeftArg()) || hasNodeCondition(varName, ((BinaryTupleOperator) expr).getRightArg()); } else { return false; } } - private boolean hasNodeCondition(Var v, ValueExpr expr) { + private boolean hasNodeCondition(String varName, ValueExpr expr) { if(expr instanceof Var) { - return v.getName().equals(((Var) expr).getName()); + return varName.equals(((Var) expr).getName()); } else if(expr instanceof UnaryValueOperator) { - return hasNodeCondition(v, ((UnaryValueOperator) expr).getArg()); + return hasNodeCondition(varName, ((UnaryValueOperator) expr).getArg()); } else if(expr instanceof BinaryValueOperator) { - return hasNodeCondition(v, ((BinaryValueOperator) expr).getLeftArg()) || hasNodeCondition(v, ((BinaryValueOperator) expr).getRightArg()); + return hasNodeCondition(varName, ((BinaryValueOperator) expr).getLeftArg()) || hasNodeCondition(varName, ((BinaryValueOperator) expr).getRightArg()); } else if(expr instanceof NAryValueOperator) { for(ValueExpr e : ((NAryValueOperator) expr).getArguments()) { - if(hasNodeCondition(v,e)) { + if(hasNodeCondition(varName,e)) { return true; } } } else if(expr instanceof FunctionCall) { for(ValueExpr e : ((FunctionCall) expr).getArgs()) { - if(hasNodeCondition(v,e)) { + if(hasNodeCondition(varName,e)) { return true; } } @@ -1033,7 +1079,7 @@ public class SQLBuilder { } - private ProjectionType getProjectionType(ValueExpr expr) { + protected ProjectionType getProjectionType(ValueExpr expr) { if(expr instanceof FunctionCall) { return opTypeToProjection(functions.get(FunctionUtil.getFunctionUri(((FunctionCall) expr).getURI())).getReturnType()); } else if(expr instanceof NAryValueOperator) { @@ -1091,7 +1137,7 @@ public class SQLBuilder { * * @return */ - public String build() throws UnsatisfiableQueryException { + public String build() { String selectClause = buildSelectClause(); String fromClause = buildFromClause(); String whereClause = buildWhereClause(); @@ -1100,20 +1146,31 @@ public class SQLBuilder { String limitClause = buildLimitClause(); - // build the query string - String queryString = - "SELECT " + selectClause + "\n " + - "FROM " + fromClause + "\n " + - "WHERE " + whereClause + "\n " + - groupClause + - orderClause + - limitClause; + StringBuilder queryString = new StringBuilder(); + queryString + .append("SELECT ").append(selectClause).append("\n ") + .append("FROM ").append(fromClause).append("\n "); + + if(whereClause.length() > 0) { + queryString.append("WHERE ").append(whereClause).append("\n "); + } + + if(groupClause.length() > 0) { + queryString.append("GROUP BY ").append(groupClause).append("\n "); + } + + if(orderClause.length() > 0) { + queryString.append("ORDER BY ").append(orderClause).append("\n "); + } + + queryString.append(limitClause); + log.debug("original SPARQL syntax tree:\n {}", query); log.debug("constructed SQL query string:\n {}",queryString); log.debug("SPARQL -> SQL node variable mappings:\n {}", variables); - return queryString; + return queryString.toString(); } } http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a33f414/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLClause.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLClause.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLClause.java new file mode 100644 index 0000000..15164ba --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLClause.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.marmotta.kiwi.sparql.builder; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * Used to mark fragments that can be used to construct table-like FROM clauses in SQL queries (triple patterns, + * subqueries, unions, ...) + * + * @author Sebastian Schaffert ([email protected]) + */ +public abstract class SQLClause { + + /** + * SQL conditions defined on this pattern; may only refer to previous or the current statement. + */ + protected List<String> conditions; + + public SQLClause() { + this.conditions = new ArrayList<>(); + } + + /** + * Build the query fragment that can be used in the FROM clause of a SQL query for representing this SPARQL construct. + * The fragment will be joined appropriately by the enclosing construct using CROSS JOIN, LEFT JOIN or normal JOIN. + * + * @return + */ + public abstract String buildFromClause(); + + /** + * Return true if the FROM clause requires parenthesis before + * @return + */ + public boolean needsParentheses() { + return false; + } + + /** + * Build the condition clause for this statement to be used in the WHERE part or the ON part of a JOIN. + * @return + */ + public String buildConditionClause() { + // the onClause consists of the filter conditions from the statement for joining/left joining with + // previous statements + StringBuilder onClause = new StringBuilder(); + + for(Iterator<String> cit = conditions.iterator(); cit.hasNext(); ) { + if(onClause.length() > 0) { + onClause.append("\n AND "); + } + onClause.append(cit.next()); + } + + return onClause.toString(); + } + + + public List<String> getConditions() { + return conditions; + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a33f414/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLFragment.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLFragment.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLFragment.java index 55920c4..11689e8 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLFragment.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLFragment.java @@ -17,6 +17,7 @@ package org.apache.marmotta.kiwi.sparql.builder; +import com.google.common.collect.Iterators; import org.openrdf.query.algebra.ValueExpr; import java.util.ArrayList; @@ -29,7 +30,7 @@ import java.util.List; * * @author Sebastian Schaffert ([email protected]) */ -public class SQLFragment { +public class SQLFragment extends SQLClause { /** * Indicate where the fragment's conditions should be placed (ON part of the JOIN clause or WHERE part of the query). @@ -46,30 +47,30 @@ public class SQLFragment { */ private List<SQLPattern> patterns; - private List<String> conditions; + private List<SQLAbstractSubquery> subqueries; private List<ValueExpr> filters; private ConditionPosition conditionPosition = ConditionPosition.JOIN; public SQLFragment() { + super(); this.patterns = new ArrayList<>(); - this.conditions = new ArrayList<>(); this.filters = new ArrayList<>(); + this.subqueries = new ArrayList<>(); } public List<SQLPattern> getPatterns() { return patterns; } - public List<String> getConditions() { - return conditions; - } - public List<ValueExpr> getFilters() { return filters; } + public List<SQLAbstractSubquery> getSubqueries() { + return subqueries; + } /** * Indicate where the fragment's conditions should be placed (ON part of the JOIN clause or WHERE part of the query). @@ -91,9 +92,9 @@ public class SQLFragment { public String buildFromClause() { StringBuilder fromClause = new StringBuilder(); - for (Iterator<SQLPattern> it = patterns.iterator(); it.hasNext(); ) { + for (Iterator<SQLClause> it = Iterators.concat(patterns.iterator(), subqueries.iterator()); it.hasNext(); ) { - SQLPattern p = it.next(); + SQLClause p = it.next(); StringBuilder conditionClause = new StringBuilder(); @@ -120,10 +121,10 @@ public class SQLFragment { // when the pattern builds a join with the nodes table and we have fragment-wide conditions, we need to // wrap the pattern's from clause in parentheses if(conditionClause.length() > 0) { - if(p.hasJoinFields()) + if(p.needsParentheses()) fromClause.append("("); fromClause.append(p.buildFromClause()); - if(p.hasJoinFields()) + if(p.needsParentheses()) fromClause.append(")"); fromClause.append(" ON ("); fromClause.append(conditionClause); @@ -154,8 +155,8 @@ public class SQLFragment { StringBuilder conditionClause = new StringBuilder(); if(conditionPosition == ConditionPosition.WHERE) { - for (Iterator<SQLPattern> it = patterns.iterator(); it.hasNext(); ) { - SQLPattern p = it.next(); + for (Iterator<SQLClause> it = Iterators.concat(patterns.iterator(), subqueries.iterator()); it.hasNext(); ) { + SQLClause p = it.next(); // in case we add the condition to the JOIN, build first the conditions for the pattern; otherwise, the // conditions for the pattern will be added to the WHERE clause http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a33f414/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLPattern.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLPattern.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLPattern.java index 3508e46..c88f1bd 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLPattern.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLPattern.java @@ -21,7 +21,9 @@ import org.openrdf.model.Resource; import org.openrdf.query.algebra.StatementPattern; import org.openrdf.query.algebra.Var; -import java.util.*; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; /** * A statement pattern translated to SQL consists of a named reference to the triple table, an indicator giving the @@ -29,7 +31,7 @@ import java.util.*; * * @author Sebastian Schaffert ([email protected]) */ -public class SQLPattern { +public class SQLPattern extends SQLClause { /** @@ -66,11 +68,6 @@ public class SQLPattern { private EnumMap<TripleColumns, Var> tripleFields = new EnumMap<>(TripleColumns.class); /** - * SQL conditions defined on this pattern; may only refer to previous or the current statement. - */ - private List<String> conditions; - - /** * Maps triple patterns from SPARQL WHERE to SQL aliases for the TRIPLES table in the FROM part. Used * to join one instance of the triples table for each triple pattern occurring in the query. */ @@ -87,8 +84,8 @@ public class SQLPattern { private List<Resource> variableContexts; public SQLPattern(String name, StatementPattern sparqlPattern) { + super(); this.name = name; - this.conditions = new ArrayList<>(); this.conditions.add(name + ".deleted = false"); this.sparqlPattern = sparqlPattern; @@ -116,6 +113,17 @@ public class SQLPattern { return joinFields.size() > 0; } + + /** + * Return true if the FROM clause requires parenthesis before + * + * @return + */ + @Override + public boolean needsParentheses() { + return hasJoinFields(); + } + public Var[] getFields() { return new Var[] { getSparqlPattern().getSubjectVar(), @@ -165,29 +173,6 @@ public class SQLPattern { return fromClause.toString(); } - /** - * Build the condition clause for this statement to be used in the WHERE part or the ON part of a JOIN. - * @return - */ - public String buildConditionClause() { - // the onClause consists of the filter conditions from the statement for joining/left joining with - // previous statements - StringBuilder onClause = new StringBuilder(); - - for(Iterator<String> cit = conditions.iterator(); cit.hasNext(); ) { - if(onClause.length() > 0) { - onClause.append("\n AND "); - } - onClause.append(cit.next()); - } - - return onClause.toString(); - } - - - public List<String> getConditions() { - return conditions; - } public String getName() { return name; http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a33f414/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLProjectionFinder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLProjectionFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLProjectionFinder.java index 46bece0..f3c2d1b 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLProjectionFinder.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLProjectionFinder.java @@ -39,18 +39,18 @@ public class SQLProjectionFinder extends QueryModelVisitorBase<RuntimeException> List<ExtensionElem> elements = new ArrayList<>(); - Var needle; + String needle; boolean found = false; - public SQLProjectionFinder(TupleExpr expr, Var needle) { + public SQLProjectionFinder(TupleExpr expr, String needle) { this.needle = needle; expr.visit(this); } @Override public void meet(ExtensionElem node) throws RuntimeException { - if(node.getName().equals(needle.getName())) { + if(node.getName().equals(needle)) { found = true; } // don't recurse to the children, as this would project non-grouped elements @@ -59,7 +59,7 @@ public class SQLProjectionFinder extends QueryModelVisitorBase<RuntimeException> @Override public void meet(Group node) throws RuntimeException { for(String g : node.getGroupBindingNames()) { - if(g.equals(needle.getName())) { + if(g.equals(needle)) { found = true; } } @@ -68,7 +68,7 @@ public class SQLProjectionFinder extends QueryModelVisitorBase<RuntimeException> @Override public void meet(Var node) throws RuntimeException { - if(node.equals(needle)) { + if(node.getName().equals(needle)) { found = true; } } http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a33f414/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLSubQuery.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLSubQuery.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLSubQuery.java new file mode 100644 index 0000000..1881122 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLSubQuery.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.marmotta.kiwi.sparql.builder; + +import org.apache.marmotta.kiwi.persistence.KiWiDialect; +import org.apache.marmotta.kiwi.sparql.exception.UnsatisfiableQueryException; +import org.openrdf.query.BindingSet; +import org.openrdf.query.Dataset; +import org.openrdf.query.algebra.SubQueryValueOperator; +import org.openrdf.query.algebra.ValueExpr; + +import java.util.HashSet; +import java.util.Set; + +/** + * Representation of a simple SPARQL->SQL subquery (no union). We can use a new SQLBuilder to construct the subquery + * and add appropriate variable mappings. + * + * @author Sebastian Schaffert ([email protected]) + */ +public class SQLSubQuery extends SQLAbstractSubquery { + + private SQLBuilder builder; + + private Set<SQLVariable> variables = new HashSet<>(); + + public SQLSubQuery(String alias, SubQueryValueOperator query, BindingSet bindings, Dataset dataset, ValueConverter converter, KiWiDialect dialect) throws UnsatisfiableQueryException { + super(alias); + + // we build a full subquery for each of the UNION's arguments + builder = new SQLBuilder(query.getSubQuery(), bindings, dataset, converter, dialect); + + for(SQLVariable svl : builder.getVariables().values()) { + variables.add(svl); + } + } + + + /** + * Return the SQL variables used by the subquery; we need this to do proper mapping in the parent query. + * + * @return + */ + @Override + public Set<SQLVariable> getQueryVariables() { + return variables; + } + + /** + * Build the query fragment that can be used in the FROM clause of a SQL query for representing this SPARQL construct. + * The fragment will be joined appropriately by the enclosing construct using CROSS JOIN, LEFT JOIN or normal JOIN. + * + * @return + */ + @Override + public String buildFromClause() { + StringBuilder fromClause = new StringBuilder(); + fromClause + .append("(") + .append(builder.build()) + .append(") AS ") + .append(alias); + return fromClause.toString(); + } + + /** + * Return the projection type of an expression in this subquery. Needed for propagation up to the parent. + * + * @param expr + * @return + */ + @Override + protected ProjectionType getProjectionType(ValueExpr expr) { + return builder.getProjectionType(expr); + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a33f414/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLUnion.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLUnion.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLUnion.java new file mode 100644 index 0000000..c681216 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLUnion.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.marmotta.kiwi.sparql.builder; + +import org.apache.marmotta.kiwi.persistence.KiWiDialect; +import org.apache.marmotta.kiwi.sparql.exception.UnsatisfiableQueryException; +import org.openrdf.query.BindingSet; +import org.openrdf.query.Dataset; +import org.openrdf.query.algebra.Union; +import org.openrdf.query.algebra.ValueExpr; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Represents a SPARQL UNION in SQL. Essentially, we translate a SPARQL UNION into a SQL subquery using UNION to + * merge the different parts. Mostly, we can use a new SQLBuilder to construct the subquery. However, what needs to be + * taken care of is that SQL UNIONS only work when all subqueries have the same columns, but in SPARQL this is not + * always the case. Furthermore, we need to map variables from the subquery to variables in the enclosing query + * (using the expressions mapping of our SQLVariable). + * + * TODO: proper variable mapping and conditions (in SQLBuilder) + * + * @author Sebastian Schaffert ([email protected]) + */ +public class SQLUnion extends SQLAbstractSubquery { + + private static Logger log = LoggerFactory.getLogger(SQLUnion.class); + + private SQLBuilder left, right; + + private Set<SQLVariable> variables = new HashSet<>(); + + public SQLUnion(String alias, Union query, BindingSet bindings, Dataset dataset, ValueConverter converter, KiWiDialect dialect) throws UnsatisfiableQueryException { + super(alias); + + // we build a full subquery for each of the UNION's arguments + left = new SQLBuilder(query.getLeftArg(), bindings, dataset, converter, dialect); + right = new SQLBuilder(query.getRightArg(), bindings, dataset, converter, dialect); + + // next we make sure that both subqueries share the same SQL variables so the SQL UNION succeeds by + // adding NULL aliases for all variables present in one but not the other + int c = 0; + Map<String,SQLVariable> leftVars = new HashMap<>(); + for(SQLVariable svl : left.getVariables().values()) { + leftVars.put(svl.getName(), svl); + } + + Map<String,SQLVariable> rightVars = new HashMap<>(); + for(SQLVariable svr : right.getVariables().values()) { + rightVars.put(svr.getName(), svr); + } + + for(SQLVariable svl : leftVars.values()) { + if(!rightVars.containsKey(svl.getName())) { + String vname = "_u_"+alias+"_" + (++c); + SQLVariable svr = new SQLVariable(svl.getName(), vname); + svr.getExpressions().add("NULL"); + svr.setProjectionType(ProjectionType.NODE); + right.getVariables().put(vname,svr); + } + variables.add(svl); + } + + for(SQLVariable svr : rightVars.values()) { + if(!leftVars.containsKey(svr.getName())) { + String vname = "_u_"+alias+"_" + (++c); + SQLVariable svl = new SQLVariable(svr.getName(), vname); + svl.getExpressions().add("NULL"); + svl.setProjectionType(ProjectionType.NODE); + left.getVariables().put(vname,svl); + } + variables.add(svr); + } + + log.debug("UNION variables: {}", variables); + } + + + /** + * Return the SQL variables used by the subquery; we need this to do proper mapping in the parent query. + * + * @return + */ + @Override + public Set<SQLVariable> getQueryVariables() { + return variables; + } + + /** + * Build the query fragment that can be used in the FROM clause of a SQL query for representing this SPARQL construct. + * The fragment will be joined appropriately by the enclosing construct using CROSS JOIN, LEFT JOIN or normal JOIN. + * + * @return + */ + @Override + public String buildFromClause() { + StringBuilder fromClause = new StringBuilder(); + fromClause + .append("((") + .append(left.build()) + .append(") UNION (") + .append(right.build()) + .append(")) AS ") + .append(alias); + return fromClause.toString(); + } + + + /** + * Return the projection type of an expression in this subquery. Needed for propagation up to the parent. + * + * @param expr + * @return + */ + @Override + protected ProjectionType getProjectionType(ValueExpr expr) { + ProjectionType r = left.getProjectionType(expr); + if(r == ProjectionType.STRING) { + r = right.getProjectionType(expr); + } + return r; + } +} + + +/* +Example: + +PREFIX foaf: <http://xmlns.com/foaf/0.1/> + +SELECT ?p ?name ?likes WHERE { + ?p foaf:name ?name . + { ?p foaf:knows ?likes } + UNION + { ?p foaf:interest ?likes } + +} + +translated to: + +SELECT SUB1.V3 AS V3, P1.object AS V2, P1.subject AS V1 + FROM triples P1 + JOIN + ( + (SELECT P2.subject AS V1, P2.object AS V3, P2.predicate AS V2 FROM triples P2 WHERE P2.deleted=false AND P2.predicate = 512217739590426624) + UNION + (SELECT P3.subject AS V1, P3.object AS V3, NULL AS V2 FROM triples P3 WHERE P3.deleted=false AND P3.predicate = 512217739326185472) + ) AS SUB1 ON (P1.subject = SUB1.V1) + WHERE P1.deleted = false + AND P1.predicate = 512217739124858880 + + + */ http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a33f414/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java index 0950796..a5dfb76 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java @@ -17,9 +17,9 @@ package org.apache.marmotta.kiwi.sparql.builder; -import org.openrdf.query.algebra.Var; - +import java.text.Collator; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; /** @@ -28,7 +28,7 @@ import java.util.List; * * @author Sebastian Schaffert ([email protected]) */ -public class SQLVariable { +public class SQLVariable { /** * A map for mapping the SPARQL variable names to internal names used for constructing SQL aliases. @@ -36,7 +36,7 @@ public class SQLVariable { */ private String name; - private Var sparqlVariable; + private String sparqlName; /** * A map for mapping SPARQL variables to field names; each variable might have one or more field names, @@ -59,9 +59,9 @@ public class SQLVariable { */ private ProjectionType projectionType = ProjectionType.NONE; - public SQLVariable(String name, Var sparqlVariable) { + public SQLVariable(String name, String sparqlName) { this.name = name; - this.sparqlVariable = sparqlVariable; + this.sparqlName = sparqlName; this.aliases = new ArrayList<>(); this.expressions = new ArrayList<>(); @@ -71,8 +71,8 @@ public class SQLVariable { return name; } - public Var getSparqlVariable() { - return sparqlVariable; + public String getSparqlName() { + return sparqlName; } public List<String> getAliases() { @@ -100,13 +100,45 @@ public class SQLVariable { } @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SQLVariable that = (SQLVariable) o; + + if (!sparqlName.equals(that.sparqlName)) return false; + + return true; + } + + @Override + public int hashCode() { + return sparqlName.hashCode(); + } + + @Override public String toString() { return "Variable{" + "SQL name='" + name + '\'' + - ", SPARQL name=" + sparqlVariable.getName() + + ", SPARQL name=" + sparqlName + ", aliases=" + aliases + ", expressions=" + expressions + ", projectionType=" + projectionType + '}'; } + + + public static final Comparator<SQLVariable> sparqlNameComparator = new Comparator<SQLVariable>() { + @Override + public int compare(SQLVariable l, SQLVariable r) { + return Collator.getInstance().compare(l.getSparqlName(), r.getSparqlName()); + } + }; + + public static final Comparator<SQLVariable> sqlNameComparator = new Comparator<SQLVariable>() { + @Override + public int compare(SQLVariable l, SQLVariable r) { + return Collator.getInstance().compare(l.getName(), r.getName()); + } + }; } http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a33f414/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/evaluation/KiWiEvaluationStrategyImpl.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/evaluation/KiWiEvaluationStrategyImpl.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/evaluation/KiWiEvaluationStrategyImpl.java index 67f3d84..89da038 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/evaluation/KiWiEvaluationStrategyImpl.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/evaluation/KiWiEvaluationStrategyImpl.java @@ -68,6 +68,34 @@ public class KiWiEvaluationStrategyImpl extends EvaluationStrategyImpl{ } @Override + public CloseableIteration<BindingSet, QueryEvaluationException> evaluate(Union union, BindingSet bindings) throws QueryEvaluationException { + if(Thread.currentThread().isInterrupted()) { + throw new QueryEvaluationException("SPARQL evaluation has already been cancelled"); + } + + if(isSupported(union)) { + log.debug("applying KiWi UNION optimizations on SPARQL query ..."); + + try { + return new ExceptionConvertingIteration<BindingSet, QueryEvaluationException>(connection.evaluateNative(union, bindings, dataset)) { + @Override + protected QueryEvaluationException convert(Exception e) { + return new QueryEvaluationException(e); + } + }; + } catch (SQLException e) { + throw new QueryEvaluationException(e.getMessage(),e); + } catch (IllegalArgumentException e) { + throw new QueryEvaluationException(e.getMessage(),e); + } catch (InterruptedException e) { + throw new QueryInterruptedException(e.getMessage()); + } + } else { + return super.evaluate(union, bindings); + } + } + + @Override public CloseableIteration<BindingSet, QueryEvaluationException> evaluate(Extension order, BindingSet bindings) throws QueryEvaluationException { if(Thread.currentThread().isInterrupted()) { throw new QueryEvaluationException("SPARQL evaluation has already been cancelled"); @@ -306,6 +334,8 @@ public class KiWiEvaluationStrategyImpl extends EvaluationStrategyImpl{ return isSupported(((Reduced) expr).getArg()); } else if(expr instanceof Distinct) { return isSupported(((Distinct) expr).getArg()); + } else if(expr instanceof Union) { + return isSupported(((Union) expr).getLeftArg()) && isSupported(((Union)expr).getRightArg()); } else if(expr instanceof Order) { for(OrderElem elem : ((Order) expr).getElements()) { if(!isSupported(elem.getExpr())) { http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a33f414/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/exception/UnsatisfiableQueryException.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/exception/UnsatisfiableQueryException.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/exception/UnsatisfiableQueryException.java index 308209a..5022bec 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/exception/UnsatisfiableQueryException.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/exception/UnsatisfiableQueryException.java @@ -22,7 +22,7 @@ package org.apache.marmotta.kiwi.sparql.exception; * * @author Sebastian Schaffert ([email protected]) */ -public class UnsatisfiableQueryException extends Exception { +public class UnsatisfiableQueryException extends RuntimeException { public UnsatisfiableQueryException() { } http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a33f414/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java index 1bd008c..0ad6a90 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java @@ -133,26 +133,26 @@ public class KiWiSparqlConnection { SQLVariable sv = vars.get(i); if(nodes[i] != null) { // resolved node - resultRow.addBinding(sv.getSparqlVariable().getName(), nodes[i]); + resultRow.addBinding(sv.getSparqlName(), nodes[i]); } else if(sv.getProjectionType() != ProjectionType.NONE) { // literal value String value = row.getString(sv.getName()); if(value != null) { switch (sv.getProjectionType()) { case URI: - resultRow.addBinding(sv.getSparqlVariable().getName(), new URIImpl(value)); + resultRow.addBinding(sv.getSparqlName(), new URIImpl(value)); break; case INT: - resultRow.addBinding(sv.getSparqlVariable().getName(), new LiteralImpl(value, XSD.Integer)); + resultRow.addBinding(sv.getSparqlName(), new LiteralImpl(value, XSD.Integer)); break; case DOUBLE: - resultRow.addBinding(sv.getSparqlVariable().getName(), new LiteralImpl(value, XSD.Double)); + resultRow.addBinding(sv.getSparqlName(), new LiteralImpl(value, XSD.Double)); break; case STRING: - resultRow.addBinding(sv.getSparqlVariable().getName(), new LiteralImpl(value)); + resultRow.addBinding(sv.getSparqlName(), new LiteralImpl(value)); break; default: - resultRow.addBinding(sv.getSparqlVariable().getName(), new LiteralImpl(value)); + resultRow.addBinding(sv.getSparqlName(), new LiteralImpl(value)); break; } } http://git-wip-us.apache.org/repos/asf/marmotta/blob/4a33f414/libraries/kiwi/kiwi-sparql/src/test/resources/org/apache/marmotta/kiwi/sparql/test/query26.sparql ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/test/resources/org/apache/marmotta/kiwi/sparql/test/query26.sparql b/libraries/kiwi/kiwi-sparql/src/test/resources/org/apache/marmotta/kiwi/sparql/test/query26.sparql index 9bf0a06..4dfd877 100644 --- a/libraries/kiwi/kiwi-sparql/src/test/resources/org/apache/marmotta/kiwi/sparql/test/query26.sparql +++ b/libraries/kiwi/kiwi-sparql/src/test/resources/org/apache/marmotta/kiwi/sparql/test/query26.sparql @@ -17,9 +17,9 @@ # PREFIX foaf: <http://xmlns.com/foaf/0.1/> -SELECT ?p ?name ?likes WHERE { +SELECT ?p ?name ?likes ?fname WHERE { ?p foaf:name ?name . - { ?p foaf:knows ?likes } + { ?p foaf:knows ?likes . ?likes foaf:knows ?fname } UNION { ?p foaf:interest ?likes }
