Repository: marmotta Updated Branches: refs/heads/develop d11b8e009 -> 8c62e9133
http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/ConditionFinder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/ConditionFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/ConditionFinder.java new file mode 100644 index 0000000..b2aef1d --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/ConditionFinder.java @@ -0,0 +1,78 @@ +/* + * 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.collect; + +import org.openrdf.query.algebra.*; +import org.openrdf.query.algebra.helpers.QueryModelVisitorBase; + +/** + * Check if a variable is used as a condition somewhere and therefore needs to be resolved. + * + * @author Sebastian Schaffert ([email protected]) + */ +public class ConditionFinder extends QueryModelVisitorBase<RuntimeException> { + + public boolean found = false; + + private String varName; + + public ConditionFinder(String varName, TupleExpr expr) { + this.varName = varName; + + expr.visit(this); + } + + public ConditionFinder(String varName, ValueExpr expr) { + this.varName = varName; + + expr.visit(this); + } + + @Override + public void meet(Var node) throws RuntimeException { + if(!found) { + found = node.getName().equals(varName); + } + } + + @Override + public void meet(Count node) throws RuntimeException { + if(!found && node.getArg() == null) { + // special case: count(*), we need the variable + found = true; + } else { + super.meet(node); + } + } + + @Override + public void meet(StatementPattern node) throws RuntimeException { + // stop, no condition + } + + + @Override + public void meet(Union node) throws RuntimeException { + // stop, subquery + } + + @Override + public void meet(Projection node) throws RuntimeException { + // stop, subquery + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/DistinctFinder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/DistinctFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/DistinctFinder.java new file mode 100644 index 0000000..31fb293 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/DistinctFinder.java @@ -0,0 +1,55 @@ +/* + * 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.collect; + +import org.openrdf.query.algebra.*; +import org.openrdf.query.algebra.helpers.QueryModelVisitorBase; + +/** +* Find distinct/reduced in a tuple expression. +* +* @author Sebastian Schaffert ([email protected]) +*/ +public class DistinctFinder extends QueryModelVisitorBase<RuntimeException> { + + public boolean distinct = false; + + public DistinctFinder(TupleExpr expr) { + expr.visit(this); + } + + @Override + public void meet(Distinct node) throws RuntimeException { + distinct = true; + } + + @Override + public void meet(Reduced node) throws RuntimeException { + distinct = true; + } + + @Override + public void meet(Projection node) throws RuntimeException { + // stop at projection, subquery + } + + @Override + public void meet(Union node) throws RuntimeException { + // stop at projection, subquery + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/ExtensionFinder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/ExtensionFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/ExtensionFinder.java new file mode 100644 index 0000000..75579d7 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/ExtensionFinder.java @@ -0,0 +1,66 @@ +/* + * 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.collect; + +import org.openrdf.query.algebra.*; +import org.openrdf.query.algebra.helpers.QueryModelVisitorBase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** +* Find the offset and limit values in a tuple expression +* +* @author Sebastian Schaffert ([email protected]) +*/ +public class ExtensionFinder extends QueryModelVisitorBase<RuntimeException> { + + private static Logger log = LoggerFactory.getLogger(ExtensionFinder.class); + + public List<ExtensionElem> elements = new ArrayList<>(); + + public ExtensionFinder(TupleExpr expr) { + expr.visit(this); + } + + @Override + public void meet(Extension node) throws RuntimeException { + // visit children before, as there might be dependencies + super.meet(node); + + for(ExtensionElem elem : node.getElements()) { + if(elem.getExpr() instanceof Var && ((Var) elem.getExpr()).getName().equals(elem.getName())) { + log.debug("ignoring self-aliasing of variable {}", elem.getName()); + } else { + elements.add(elem); + } + } + } + + @Override + public void meet(Projection node) throws RuntimeException { + // stop here, this is a subquery in SQL + } + + @Override + public void meet(Union node) throws RuntimeException { + // stop here, this is a subquery in SQL + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/GroupFinder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/GroupFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/GroupFinder.java new file mode 100644 index 0000000..81fa0bf --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/GroupFinder.java @@ -0,0 +1,58 @@ +/* + * 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.collect; + +import org.openrdf.query.algebra.*; +import org.openrdf.query.algebra.helpers.QueryModelVisitorBase; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** +* Find the offset and limit values in a tuple expression +* +* @author Sebastian Schaffert ([email protected]) +*/ +public class GroupFinder extends QueryModelVisitorBase<RuntimeException> { + + public Set<String> bindings = new HashSet<>(); + public List<GroupElem> elements = new ArrayList<>(); + + public GroupFinder(TupleExpr expr) { + expr.visit(this); + } + + @Override + public void meet(Group node) throws RuntimeException { + bindings.addAll(node.getGroupBindingNames()); + elements.addAll(node.getGroupElements()); + super.meet(node); + } + + @Override + public void meet(Projection node) throws RuntimeException { + // stop at projection, subquery + } + + @Override + public void meet(Union node) throws RuntimeException { + // stop at union, subquery + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/LimitFinder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/LimitFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/LimitFinder.java new file mode 100644 index 0000000..e815448 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/LimitFinder.java @@ -0,0 +1,58 @@ +/* + * 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.collect; + +import org.openrdf.query.algebra.Projection; +import org.openrdf.query.algebra.Slice; +import org.openrdf.query.algebra.TupleExpr; +import org.openrdf.query.algebra.Union; +import org.openrdf.query.algebra.helpers.QueryModelVisitorBase; + +/** +* Find the offset and limit values in a tuple expression +* +* @author Sebastian Schaffert ([email protected]) +*/ +public class LimitFinder extends QueryModelVisitorBase<RuntimeException> { + + public long limit = -1, offset = -1; + + public LimitFinder(TupleExpr expr) { + expr.visit(this); + } + + @Override + public void meet(Slice node) throws RuntimeException { + if(node.hasLimit()) + limit = node.getLimit(); + if(node.hasOffset()) + offset = node.getOffset(); + } + + + @Override + public void meet(Projection node) throws RuntimeException { + // stop at projection, subquery + } + + @Override + public void meet(Union node) throws RuntimeException { + // stop at projection, subquery + } + +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/LiteralTypeExpressionFinder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/LiteralTypeExpressionFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/LiteralTypeExpressionFinder.java new file mode 100644 index 0000000..d859205 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/LiteralTypeExpressionFinder.java @@ -0,0 +1,69 @@ +/* + * 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.collect; + +import org.apache.marmotta.kiwi.sparql.builder.OPTypes; +import org.apache.marmotta.kiwi.sparql.function.NativeFunction; +import org.apache.marmotta.kiwi.sparql.function.NativeFunctionRegistry; +import org.openrdf.query.algebra.FunctionCall; +import org.openrdf.query.algebra.QueryModelNode; +import org.openrdf.query.algebra.ValueExpr; +import org.openrdf.query.algebra.Var; +import org.openrdf.query.algebra.helpers.QueryModelVisitorBase; + +/** + * Functions that return a string literal do so with the string literal of the same kind as the first + * argument (simple literal, plain literal with same language tag, xsd:string). This visitor + * tries finding the relevant subexpression in a complex value expression. + * + * @author Sebastian Schaffert ([email protected]) + */ +public class LiteralTypeExpressionFinder extends QueryModelVisitorBase<RuntimeException> { + + public Var expr = null; + + public LiteralTypeExpressionFinder(ValueExpr expr) { + expr.visit(this); + } + + @Override + public void meet(FunctionCall node) throws RuntimeException { + NativeFunction nf = NativeFunctionRegistry.getInstance().get(node.getURI()); + if(node.getArgs().size() > 0 && nf.getReturnType() == OPTypes.STRING) { + node.getArgs().get(0).visit(this); + } + // otherwise stop here, the function call hides the type and language anyways + } + + @Override + public void meet(Var node) throws RuntimeException { + expr = node; + } + + /** + * Method called by all of the other <tt>meet</tt> methods that are not + * overridden in subclasses. This method can be overridden in subclasses to + * define default behaviour when visiting nodes. The default behaviour of + * this method is to visit the node's children. + * + * @param node The node that is being visited. + */ + @Override + protected void meetNode(QueryModelNode node) throws RuntimeException { + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/OPTypeFinder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/OPTypeFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/OPTypeFinder.java new file mode 100644 index 0000000..5dc7938 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/OPTypeFinder.java @@ -0,0 +1,135 @@ +/* + * 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.collect; + +import org.apache.commons.lang3.StringUtils; +import org.apache.marmotta.commons.sesame.model.Namespaces; +import org.apache.marmotta.kiwi.sparql.builder.OPTypes; +import org.apache.marmotta.kiwi.sparql.function.NativeFunction; +import org.apache.marmotta.kiwi.sparql.function.NativeFunctionRegistry; +import org.openrdf.model.Literal; +import org.openrdf.query.algebra.*; +import org.openrdf.query.algebra.helpers.QueryModelVisitorBase; + +import java.util.ArrayList; +import java.util.List; + +/** + * Determine the operand type of a value expression. Get the coerced value by calling coerce(). + * + * @author Sebastian Schaffert ([email protected]) + */ +public class OPTypeFinder extends QueryModelVisitorBase<RuntimeException> { + + public List<OPTypes> optypes = new ArrayList<>(); + + + + public OPTypeFinder(ValueExpr expr) { + expr.visit(this); + } + + @Override + public void meet(ValueConstant node) throws RuntimeException { + if(node.getValue() instanceof Literal) { + Literal l = (Literal)node.getValue(); + String type = l.getDatatype() != null ? l.getDatatype().stringValue() : null; + + if(StringUtils.equals(Namespaces.NS_XSD + "double", type) + || StringUtils.equals(Namespaces.NS_XSD + "float", type) + || StringUtils.equals(Namespaces.NS_XSD + "decimal", type)) { + optypes.add(OPTypes.DOUBLE); + } else if(StringUtils.equals(Namespaces.NS_XSD + "integer", type) + || StringUtils.equals(Namespaces.NS_XSD + "long", type) + || StringUtils.equals(Namespaces.NS_XSD + "int", type) + || StringUtils.equals(Namespaces.NS_XSD + "short", type) + || StringUtils.equals(Namespaces.NS_XSD + "nonNegativeInteger", type) + || StringUtils.equals(Namespaces.NS_XSD + "nonPositiveInteger", type) + || StringUtils.equals(Namespaces.NS_XSD + "negativeInteger", type) + || StringUtils.equals(Namespaces.NS_XSD + "positiveInteger", type) + || StringUtils.equals(Namespaces.NS_XSD + "unsignedLong", type) + || StringUtils.equals(Namespaces.NS_XSD + "unsignedShort", type) + || StringUtils.equals(Namespaces.NS_XSD + "byte", type) + || StringUtils.equals(Namespaces.NS_XSD + "unsignedByte", type)) { + optypes.add(OPTypes.INT); + } else if(StringUtils.equals(Namespaces.NS_XSD + "dateTime", type) + || StringUtils.equals(Namespaces.NS_XSD + "date", type) + || StringUtils.equals(Namespaces.NS_XSD + "time", type)) { + optypes.add(OPTypes.DATE); + } else { + optypes.add(OPTypes.STRING); + } + } else { + optypes.add(OPTypes.STRING); + } + } + + @Override + public void meet(Str node) throws RuntimeException { + optypes.add(OPTypes.STRING); + } + + @Override + public void meet(Lang node) throws RuntimeException { + optypes.add(OPTypes.STRING); + } + + @Override + public void meet(LocalName node) throws RuntimeException { + optypes.add(OPTypes.STRING); + } + + @Override + public void meet(Label node) throws RuntimeException { + optypes.add(OPTypes.STRING); + } + + + @Override + public void meet(FunctionCall fc) throws RuntimeException { + NativeFunction nf = NativeFunctionRegistry.getInstance().get(fc.getURI()); + + if (nf != null) { + optypes.add(nf.getReturnType()); + } + } + + + public OPTypes coerce() { + OPTypes left = OPTypes.ANY; + + for(OPTypes right : optypes) { + if(left == OPTypes.ANY) { + left = right; + } else if(right == OPTypes.ANY) { + // keep left + } else if(left == right) { + // keep left + } else if( (left == OPTypes.INT && right == OPTypes.DOUBLE) || (left == OPTypes.DOUBLE && right == OPTypes.INT)) { + left = OPTypes.DOUBLE; + } else if( (left == OPTypes.STRING) || (right == OPTypes.STRING)) { + left = OPTypes.STRING; + } else { + throw new IllegalArgumentException("unsupported type coercion: " + left + " and " + right); + } + } + return left; + } + + +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/OrderFinder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/OrderFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/OrderFinder.java new file mode 100644 index 0000000..dc5d2f2 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/OrderFinder.java @@ -0,0 +1,56 @@ +/* + * 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.collect; + +import org.openrdf.query.algebra.*; +import org.openrdf.query.algebra.helpers.QueryModelVisitorBase; + +import java.util.ArrayList; +import java.util.List; + +/** +* Find the offset and limit values in a tuple expression +* +* @author Sebastian Schaffert ([email protected]) +*/ +public class OrderFinder extends QueryModelVisitorBase<RuntimeException> { + + public List<OrderElem> elements = new ArrayList<>(); + + public OrderFinder(TupleExpr expr) { + expr.visit(this); + } + + @Override + public void meet(Order node) throws RuntimeException { + elements.addAll(node.getElements()); + } + + + + @Override + public void meet(Projection node) throws RuntimeException { + // stop at projection, subquery + } + + @Override + public void meet(Union node) throws RuntimeException { + // stop at projection, subquery + } + +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/PatternCollector.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/PatternCollector.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/PatternCollector.java new file mode 100644 index 0000000..e29e619 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/PatternCollector.java @@ -0,0 +1,113 @@ +/* + * 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.collect; + +import org.apache.marmotta.kiwi.persistence.KiWiDialect; +import org.apache.marmotta.kiwi.sparql.builder.ValueConverter; +import org.apache.marmotta.kiwi.sparql.builder.model.SQLFragment; +import org.apache.marmotta.kiwi.sparql.builder.model.SQLPattern; +import org.apache.marmotta.kiwi.sparql.builder.model.SQLSubQuery; +import org.apache.marmotta.kiwi.sparql.builder.model.SQLUnion; +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; +import java.util.Set; + +/** +* Collect all statement patterns in a tuple expression. +* +* @author Sebastian Schaffert ([email protected]) +*/ +public class PatternCollector extends QueryModelVisitorBase<RuntimeException> { + + public LinkedList<SQLFragment> parts = new LinkedList<>(); + + int counter = 0; + + + private BindingSet bindings; + private Dataset dataset; + private ValueConverter converter; + private KiWiDialect dialect; + private Set<String> projectedVars; + private String prefix; + + public PatternCollector(TupleExpr expr, BindingSet bindings, Dataset dataset, ValueConverter converter, KiWiDialect dialect, Set<String> projectedVars, String prefix) { + this.bindings = bindings; + this.dataset = dataset; + this.converter = converter; + this.dialect = dialect; + this.projectedVars = projectedVars; + this.prefix = prefix; + + parts.push(new SQLFragment()); + expr.visit(this); + } + + @Override + public void meet(StatementPattern node) throws RuntimeException { + parts.getLast().getPatterns().add(new SQLPattern(prefix + "P" + (++counter), node)); + + super.meet(node); + } + + @Override + public void meet(LeftJoin node) throws RuntimeException { + node.getLeftArg().visit(this); + parts.addLast(new SQLFragment()); + if(node.hasCondition()) { + parts.getLast().getFilters().add(node.getCondition()); + } + node.getRightArg().visit(this); + + } + + + @Override + public void meet(Filter node) throws RuntimeException { + parts.getLast().getFilters().add(node.getCondition()); + + if(node.getArg() instanceof Group) { + parts.getLast().setConditionPosition(SQLFragment.ConditionPosition.HAVING); + } + + 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(prefix + "U" + (++counter),node, bindings, dataset, converter, dialect)); + } + + @Override + public void meet(Projection node) throws RuntimeException { + // subqueries are represented with a projection inside a JOIN; we don't continue collection + + parts.getLast().getSubqueries().add(new SQLSubQuery(prefix + "S" + (++counter), node, bindings, dataset, converter, dialect, projectedVars)); + } + + @Override + public void meet(Exists node) throws RuntimeException { + // stop at exists, it is treated as a subquery in the condition part + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/SQLProjectionFinder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/SQLProjectionFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/SQLProjectionFinder.java new file mode 100644 index 0000000..c190e20 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/SQLProjectionFinder.java @@ -0,0 +1,88 @@ +/* + * 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.collect; + +import org.openrdf.query.algebra.*; +import org.openrdf.query.algebra.helpers.QueryModelVisitorBase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** +* Find the offset and limit values in a tuple expression +* +* @author Sebastian Schaffert ([email protected]) +*/ +public class SQLProjectionFinder extends QueryModelVisitorBase<RuntimeException> { + + private static Logger log = LoggerFactory.getLogger(SQLProjectionFinder.class); + + public List<ExtensionElem> elements = new ArrayList<>(); + + private String needle; + + public boolean found = false; + + 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)) { + found = true; + } + // don't recurse to the children, as this would project non-grouped elements + } + + @Override + public void meet(Group node) throws RuntimeException { + for(String g : node.getGroupBindingNames()) { + if(g.equals(needle)) { + found = true; + } + } + // don't recurse to the children, as this would project non-grouped elements + } + + @Override + public void meet(Var node) throws RuntimeException { + if(node.getName().equals(needle)) { + found = true; + } + } + + + @Override + public void meet(Projection node) throws RuntimeException { + for(ProjectionElem elem : node.getProjectionElemList().getElements()) { + if(elem.getSourceName().equals(needle)) { + found = true; + } + } + // stop at projection, subquery + } + + @Override + public void meet(Union node) throws RuntimeException { + // stop at union, subquery + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/VariableFinder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/VariableFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/VariableFinder.java new file mode 100644 index 0000000..2cc7f76 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/collect/VariableFinder.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.collect; + +import org.openrdf.query.algebra.Projection; +import org.openrdf.query.algebra.TupleExpr; +import org.openrdf.query.algebra.Var; +import org.openrdf.query.algebra.helpers.QueryModelVisitorBase; + +import java.util.HashSet; +import java.util.Set; + +/** +* Find distinct/reduced in a tuple expression. +* +* @author Sebastian Schaffert ([email protected]) +*/ +public class VariableFinder extends QueryModelVisitorBase<RuntimeException> { + + public Set<Var> variables = new HashSet<>(); + + public VariableFinder(TupleExpr expr) { + expr.visit(this); + } + + + @Override + public void meet(Var node) throws RuntimeException { + variables.add(node); + } + + + @Override + public void meet(Projection node) throws RuntimeException { + // stop at projection, subquery + } + +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLAbstractSubquery.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLAbstractSubquery.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLAbstractSubquery.java new file mode 100644 index 0000000..106b291 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLAbstractSubquery.java @@ -0,0 +1,124 @@ +/* + * 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.model; + +import java.util.HashSet; +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; + + + /** + * This set contains variable names describing those variables of the subquery that need to be joined with the NODES + * table. This is typically the case when there is a condition or function referring to the actual value of the node + * and not only the ID. The joined NODES table will be aliased with {alias}_{name}. + */ + private Set<VariableMapping> joinFields = new HashSet<>(); + + + public SQLAbstractSubquery(String alias) { + this.alias = alias; + } + + public String getAlias() { + return alias; + } + + + public Set<VariableMapping> getJoinFields() { + return joinFields; + } + + /** + * Return true when the pattern involves JOINs with the NODES table; in this case we need to enclose the + * FROM clause with parentheses before joining with previous clauses. + * @return + */ + public boolean hasJoinFields() { + return joinFields.size() > 0; + } + + + /** + * Return true if the FROM clause requires parenthesis before + * + * @return + */ + @Override + public boolean needsParentheses() { + return hasJoinFields(); + } + + + /** + * 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(); + + /** + * Mapping for a variable between its name in a subquery and its name in the parent query. Used for resolving join + * fields in subqueries. + * + * @author Sebastian Schaffert ([email protected]) + */ + public static class VariableMapping { + + private String parentName, subqueryName; + + public VariableMapping(String parentName, String subqueryName) { + this.parentName = parentName; + this.subqueryName = subqueryName; + } + + public String getParentName() { + return parentName; + } + + public String getSubqueryName() { + return subqueryName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + VariableMapping that = (VariableMapping) o; + + if (!parentName.equals(that.parentName)) return false; + if (!subqueryName.equals(that.subqueryName)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = parentName.hashCode(); + result = 31 * result + subqueryName.hashCode(); + return result; + } + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLClause.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLClause.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLClause.java new file mode 100644 index 0000000..b8bc4db --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLClause.java @@ -0,0 +1,81 @@ +/* + * 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.model; + +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(); ) { + String next = cit.next(); + if(onClause.length() > 0 && next.length() > 0) { + onClause.append("\n AND "); + } + onClause.append(next); + } + + return onClause.toString(); + } + + + public List<String> getConditions() { + return conditions; + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLFragment.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLFragment.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLFragment.java new file mode 100644 index 0000000..b76fdbc --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLFragment.java @@ -0,0 +1,193 @@ +/* + * 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.model; + +import com.google.common.collect.Iterators; +import org.openrdf.query.algebra.ValueExpr; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +/** + * An SQL fragment is a part of the SQL query where all patterns are joinedwith INNER JOINS and not LEFT JOINS. Several + * patterns are then joined using a left join. + * + * @author Sebastian Schaffert ([email protected]) + */ +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). + * This distinction is necessary when OPTIONAL constructs are used, i.e. the created SQL uses LEFT JOINs. We cannot + * always place it in JOIN conditions, because the first pattern will not have a JOIN. + */ + public static enum ConditionPosition { + JOIN, WHERE, HAVING + }; + + private static Random singletonSetGenerator = new Random(); + + /** + * The patterns contained in this fragment. All patterns are joined using an INNER JOIN. + */ + private List<SQLPattern> patterns; + + private List<SQLAbstractSubquery> subqueries; + + private List<ValueExpr> filters; + + private ConditionPosition conditionPosition = ConditionPosition.JOIN; + + public SQLFragment() { + super(); + this.patterns = new ArrayList<>(); + this.filters = new ArrayList<>(); + this.subqueries = new ArrayList<>(); + } + + public List<SQLPattern> getPatterns() { + return patterns; + } + + 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). + * For the first fragment in a query this will always be WHERE, while for all other fragments it should be JOIN. Note + * that JOIN is strictly necessary for all fragments that are OPTIONAL. + */ + public void setConditionPosition(ConditionPosition conditionPosition) { + this.conditionPosition = conditionPosition; + } + + public ConditionPosition getConditionPosition() { + return conditionPosition; + } + + /** + * Build the FROM clause by joining together all patterns appropriately and adding the filter conditions + * @return + */ + public String buildFromClause() { + StringBuilder fromClause = new StringBuilder(); + + if(patterns.size() > 0 || subqueries.size() > 0) { + for (Iterator<SQLClause> it = Iterators.concat(patterns.iterator(), subqueries.iterator()); it.hasNext(); ) { + + SQLClause p = it.next(); + + + StringBuilder conditionClause = new StringBuilder(); + + // 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 + if (conditionPosition == ConditionPosition.JOIN) { + conditionClause.append(p.buildConditionClause()); + } + + + // in case the pattern is the last of the fragment, also add the filter conditions of the fragment (TODO: verify this does indeed the right thing) + if (conditionPosition == ConditionPosition.JOIN && !it.hasNext()) { + // if this is the last pattern of the fragment, add the filter conditions + for (Iterator<String> cit = getConditions().iterator(); cit.hasNext(); ) { + String next = cit.next(); + if (conditionClause.length() > 0 && next.length() > 0) { + conditionClause.append("\n AND "); + } + conditionClause.append(next); + } + } + + + // 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.needsParentheses()) + fromClause.append("("); + fromClause.append(p.buildFromClause()); + if (p.needsParentheses()) + fromClause.append(")"); + fromClause.append(" ON ("); + fromClause.append(conditionClause); + fromClause.append(")"); + + } else { + fromClause.append(p.buildFromClause()); + } + + + if (it.hasNext()) { + if (conditionPosition == ConditionPosition.JOIN) { + fromClause.append("\n JOIN \n "); + } else { + fromClause.append("\n CROSS JOIN \n "); + } + } + } + } else { + fromClause.append("(SELECT true) AS _EMPTY"+singletonSetGenerator.nextInt(1000)); + } + + return fromClause.toString(); + } + + /** + * Build the combined condition clause for this fragment. This will be the empty string when the conditionPosition is JOIN. + * @return + */ + @Override + public String buildConditionClause() { + StringBuilder conditionClause = new StringBuilder(); + + if(conditionPosition == ConditionPosition.WHERE || conditionPosition == ConditionPosition.HAVING) { + 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 + if (conditionClause.length() > 0) { + conditionClause.append("\n AND "); + } + conditionClause.append(p.buildConditionClause()); + + } + } + + if(conditionPosition == ConditionPosition.WHERE) { + // in case the pattern is the last of the fragment, also add the filter conditions of the fragment + // if this is the last pattern of the fragment, add the filter conditions + for(Iterator<String> cit = getConditions().iterator(); cit.hasNext(); ) { + String next = cit.next(); + if(conditionClause.length() > 0 && next.length() > 0) { + conditionClause.append("\n AND "); + } + conditionClause.append(next); + } + } + + return conditionClause.toString(); + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLPattern.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLPattern.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLPattern.java new file mode 100644 index 0000000..82ed0f9 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLPattern.java @@ -0,0 +1,196 @@ +/* + * 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.model; + +import org.openrdf.model.Resource; +import org.openrdf.query.algebra.StatementPattern; +import org.openrdf.query.algebra.Var; + +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 + * join type (JOIN or LEFT JOIN), and any number of filter conditions + * + * @author Sebastian Schaffert ([email protected]) + */ +public class SQLPattern extends SQLClause { + + + /** + * Describe the different columns of the triple table that we might need to join with + */ + public static enum TripleColumns { + SUBJECT ("subject"), + PREDICATE("predicate"), + OBJECT ("object"), + CONTEXT ("context"); + + TripleColumns(String fieldName) { + this.fieldName = fieldName; + } + + private final String fieldName; + + public String getFieldName() { + return fieldName; + } + }; + + /** + * This map contains mappings from column to variable names. If for a given column an entry is contained in the + * map, the statement requires to join the TRIPLE table on this field with the NODES table. This is typically + * the case when there is a condition or function referring to the actual value of the node and not only the ID. + * The joined NODES table will be aliased with the variable name contained as value for the field. + */ + private EnumMap<TripleColumns, String> joinFields = new EnumMap<>(TripleColumns.class); + + /** + * A map containing references to the variables used in the triple fields. + */ + private EnumMap<TripleColumns, Var> tripleFields = new EnumMap<>(TripleColumns.class); + + /** + * 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. + */ + private String name; + + /** + * A reference to the SPARQL statement pattern represented by this SQLPattern + */ + private StatementPattern sparqlPattern; + + /** + * Alternative context values for each variable used in the context part of a pattern + */ + private List<Resource> variableContexts; + + public SQLPattern(String name, StatementPattern sparqlPattern) { + super(); + this.name = name; + this.conditions.add(name + ".deleted = false"); + this.sparqlPattern = sparqlPattern; + + tripleFields.put(TripleColumns.SUBJECT, sparqlPattern.getSubjectVar()); + tripleFields.put(TripleColumns.PREDICATE, sparqlPattern.getPredicateVar()); + tripleFields.put(TripleColumns.OBJECT, sparqlPattern.getObjectVar()); + tripleFields.put(TripleColumns.CONTEXT, sparqlPattern.getContextVar()); + } + + /** + * Set the variable name (alias for the NODES table) for the given column to "varName". + * @param col + * @param varName + */ + public void setJoinField(TripleColumns col, String varName) { + joinFields.put(col,varName); + } + + /** + * Return true when the pattern involves JOINs with the NODES table; in this case we need to enclose the + * FROM clause with parentheses before joining with previous clauses. + * @return + */ + public boolean hasJoinFields() { + 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(), + getSparqlPattern().getPredicateVar(), + getSparqlPattern().getObjectVar(), + getSparqlPattern().getContextVar() + }; + } + + public EnumMap<TripleColumns, Var> getTripleFields() { + return tripleFields; + } + + /** + * Create the clause to be used in the FROM part to represent this pattern and return it. The name and join fields + * need to be set before so this produces the correct output. + * + * This method works as follows: + * - for the statement pattern, it adds a reference to the TRIPLES table and aliases it with the pattern name + * - for each field of the pattern whose value is used in conditions or functions, an INNER JOIN with the NODES table + * is added to retrieve the actual node with its values; the NODES table is aliased using the variable name set in + * setJoinField() + * + * @return + */ + public String buildFromClause() { + // the joinClause consists of a reference to the triples table and possibly inner joins with the + // nodes table in case we need to verify node values, e.g. in a filter + StringBuilder fromClause = new StringBuilder(); + + + fromClause.append("triples " + name); + + + for(Map.Entry<TripleColumns,String> colEntry : joinFields.entrySet()) { + TripleColumns col = colEntry.getKey(); + String var = colEntry.getValue(); + + fromClause.append("\n INNER JOIN nodes AS "); + fromClause.append(name + "_" + col.getFieldName() + "_" + var); + + fromClause.append(" ON " + name + "." + col.getFieldName() + " = " + name + "_" + col.getFieldName() + "_" + var + ".id "); + + } + + + return fromClause.toString(); + } + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public StatementPattern getSparqlPattern() { + return sparqlPattern; + } + + public List<Resource> getVariableContexts() { + return variableContexts; + } + + public void setVariableContexts(List<Resource> variableContexts) { + this.variableContexts = variableContexts; + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLSubQuery.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLSubQuery.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLSubQuery.java new file mode 100644 index 0000000..891c86b --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLSubQuery.java @@ -0,0 +1,103 @@ +/* + * 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.model; + +import org.apache.marmotta.kiwi.persistence.KiWiDialect; +import org.apache.marmotta.kiwi.sparql.builder.SQLBuilder; +import org.apache.marmotta.kiwi.sparql.builder.ValueConverter; +import org.apache.marmotta.kiwi.sparql.exception.UnsatisfiableQueryException; +import org.openrdf.query.BindingSet; +import org.openrdf.query.Dataset; +import org.openrdf.query.algebra.Projection; +import org.openrdf.query.algebra.ProjectionElem; + +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, Projection query, BindingSet bindings, Dataset dataset, ValueConverter converter, KiWiDialect dialect, Set<String> parentProjectedVars) throws UnsatisfiableQueryException { + super(alias); + + Set<String> projectedVars = new HashSet<>(parentProjectedVars); + // count projected variables + for(ProjectionElem elem : query.getProjectionElemList().getElements()) { + projectedVars.add(elem.getSourceName()); + } + + + + // we build a full subquery for each of the UNION's arguments + builder = new SQLBuilder(query.getArg(), bindings, dataset, converter, dialect, projectedVars); + + for(SQLVariable svl : builder.getVariables().values()) { + if(projectedVars.contains(svl.getSparqlName())) { + 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); + + for(VariableMapping var : getJoinFields()) { + fromClause.append(" LEFT JOIN nodes AS "); // outer join because binding might be NULL + fromClause.append(alias).append("_").append(var.getParentName()); + + fromClause + .append(" ON ").append(alias).append(".").append(var.getSubqueryName()) + .append(" = ").append(alias).append("_").append(var.getParentName()).append(".id "); + } + + return fromClause.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLUnion.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLUnion.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLUnion.java new file mode 100644 index 0000000..5a4e0ac --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLUnion.java @@ -0,0 +1,218 @@ +/* + * 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.model; + +import org.apache.marmotta.kiwi.persistence.KiWiDialect; +import org.apache.marmotta.kiwi.sparql.builder.ProjectionType; +import org.apache.marmotta.kiwi.sparql.builder.SQLBuilder; +import org.apache.marmotta.kiwi.sparql.builder.ValueConverter; +import org.apache.marmotta.kiwi.sparql.exception.UnsatisfiableQueryException; +import org.openrdf.query.BindingSet; +import org.openrdf.query.Dataset; +import org.openrdf.query.algebra.Projection; +import org.openrdf.query.algebra.ProjectionElem; +import org.openrdf.query.algebra.TupleExpr; +import org.openrdf.query.algebra.Union; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +/** + * 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); + + Set<String> leftProjected = getProjectedVariables(query.getLeftArg()); + Set<String> rightProjected = getProjectedVariables(query.getRightArg()); + + // we build a full subquery for each of the UNION's arguments + left = new SQLBuilder(query.getLeftArg(), bindings, dataset, converter, dialect, leftProjected); + right = new SQLBuilder(query.getRightArg(), bindings, dataset, converter, dialect, rightProjected); + + // 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()) { + if(leftProjected.size() == 0 || leftProjected.contains(svl.getSparqlName())) { + leftVars.put(svl.getSparqlName(), svl); + } + } + + Map<String,SQLVariable> rightVars = new HashMap<>(); + for(SQLVariable svr : right.getVariables().values()) { + if(rightProjected.size() == 0 || rightProjected.contains(svr.getSparqlName())) { + rightVars.put(svr.getSparqlName(), svr); + } + } + + // we have to homogenize variable names in both subqueries and make sure they have the same number of columns + Map<String,String> sparqlToSQL = new HashMap<>(); + for(SQLVariable svl : left.getVariables().values()) { + if(leftProjected.size() == 0 || leftProjected.contains(svl.getSparqlName())) { + if (sparqlToSQL.containsKey(svl.getSparqlName())) { + svl.setName(sparqlToSQL.get(svl.getSparqlName())); + } else { + svl.setName("U" + (++c)); + sparqlToSQL.put(svl.getSparqlName(), svl.getName()); + } + } + } + for(SQLVariable svr : right.getVariables().values()) { + if(rightProjected.size() == 0 || rightProjected.contains(svr.getSparqlName())) { + if (sparqlToSQL.containsKey(svr.getSparqlName())) { + svr.setName(sparqlToSQL.get(svr.getSparqlName())); + } else { + svr.setName("U" + (++c)); + sparqlToSQL.put(svr.getSparqlName(), svr.getName()); + } + } + } + + + for(SQLVariable svl : leftVars.values()) { + if(!rightVars.containsKey(svl.getSparqlName())) { + SQLVariable svr = new SQLVariable(svl.getName(), svl.getSparqlName()); + svr.getExpressions().add("NULL"); + svr.setProjectionType(ProjectionType.NODE); + right.getVariables().put(svl.getSparqlName(),svr); + + if(rightProjected.size() > 0) { + right.getProjectedVars().add(svl.getSparqlName()); + } + } + variables.add(svl); + } + + for(SQLVariable svr : rightVars.values()) { + if(!leftVars.containsKey(svr.getSparqlName())) { + SQLVariable svl = new SQLVariable(svr.getName(), svr.getSparqlName()); + svl.getExpressions().add("NULL"); + svl.setProjectionType(ProjectionType.NODE); + left.getVariables().put(svr.getSparqlName(),svl); + + if(leftProjected.size() > 0) { + left.getProjectedVars().add(svr.getSparqlName()); + } + } + 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); + + for(VariableMapping var : getJoinFields()) { + fromClause.append(" LEFT JOIN nodes AS "); // outer join because binding might be NULL + fromClause.append(alias + "_" + var.getParentName()); + + fromClause.append(" ON " + alias + "." + var.getSubqueryName() + " = " + alias + "_" + var.getParentName() + ".id "); + } + + return fromClause.toString(); + } + + + private Set<String> getProjectedVariables(TupleExpr expr) { + if(expr instanceof Projection) { + Projection projection = (Projection)expr; + Set<String> projectedVars = new HashSet<>(); + for (ProjectionElem elem : projection.getProjectionElemList().getElements()) { + projectedVars.add(elem.getSourceName()); + } + return projectedVars; + } else { + return Collections.EMPTY_SET; + } + } +} + + +/* +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/8c62e913/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLVariable.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLVariable.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLVariable.java new file mode 100644 index 0000000..b535984 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/model/SQLVariable.java @@ -0,0 +1,197 @@ +/* + * 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.model; + +import org.apache.marmotta.kiwi.sparql.builder.ProjectionType; +import org.openrdf.query.algebra.ValueExpr; + +import java.text.Collator; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * Representation of a SPARQL variable in SQL. A SPARQL variable will always be translated into a column alias + * for either a subject/predicate/object/context field of a triple or a more complex expression (e.g. function evaluation). + * + * @author Sebastian Schaffert ([email protected]) + */ +public class SQLVariable implements Cloneable{ + + /** + * A map for mapping the SPARQL variable names to internal names used for constructing SQL aliases. + * Will look like { ?x -> "V1", ?y -> "V2", ... } + */ + private String name; + + private String sparqlName; + + /** + * A map for mapping SPARQL variables to field names; each variable might have one or more field names, + * depending on the number of patterns it occurs in; will look like + * { ?x -> ["P1_V1", "P2_V1"], ?y -> ["P2_V2"], ... } + */ + private String alias; + + + /** + * A map for mapping SPARQL variables to database expressions (e.g. node ID selectors). A node ID can occur either as + * primary key in the NODES table or in the subject, predicate, object and context fields of a pattern. An expression + * can be e.g. a function evaluation. + */ + private List<String> expressions; + + /** + * A list of value expressions bound to this variable; this is needed in case the variable is used in a filter or + * ORDER BY, because then we need to determine the type. + */ + private List<ValueExpr> bindings; + + + /** + * Set to something else than NONE when this variable is contained in the SELECT part of the query, i.e. needs to be projected. + * Decides on how the variable will be projected (as node -> ID, as value -> string or numeric field) + */ + private ProjectionType projectionType = ProjectionType.NONE; + + /** + * The expression to project the type for the literal that will be bound to this variable, e.g. in case + * the main expression is a function call and the type should be preserved. + */ + private String literalTypeExpression = null; + + /** + * The expression to project the language for the literal that will be bound to this variable, e.g. in case + * the main expression is a function call and the language should be preserved. + */ + private String literalLangExpression = null; + + public SQLVariable(String name, String sparqlName) { + this.name = name; + this.sparqlName = sparqlName; + + this.bindings = new ArrayList<>(); + this.expressions = new ArrayList<>(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSparqlName() { + return sparqlName; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public List<ValueExpr> getBindings() { + return bindings; + } + + public List<String> getExpressions() { + return expressions; + } + + public ProjectionType getProjectionType() { + return projectionType; + } + + public void setProjectionType(ProjectionType projectionType) { + this.projectionType = projectionType; + } + + public String getLiteralTypeExpression() { + return literalTypeExpression; + } + + public void setLiteralTypeExpression(String literalTypeExpression) { + this.literalTypeExpression = literalTypeExpression; + } + + public String getLiteralLangExpression() { + return literalLangExpression; + } + + public void setLiteralLangExpression(String literalLangExpression) { + this.literalLangExpression = literalLangExpression; + } + + @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=" + sparqlName + + ", alias=" + alias + + ", 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()); + } + }; + + @Override + public Object clone() throws CloneNotSupportedException { + SQLVariable clone = new SQLVariable(getName(), getSparqlName()); + clone.projectionType = projectionType; + clone.getExpressions().addAll(expressions); + clone.alias = alias; + clone.getBindings().addAll(bindings); + + return clone; + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/8c62e913/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 0c3322a..f035d56 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 @@ -30,7 +30,7 @@ import org.apache.marmotta.kiwi.persistence.util.ResultTransformerFunction; import org.apache.marmotta.kiwi.sail.KiWiValueFactory; import org.apache.marmotta.kiwi.sparql.builder.ProjectionType; import org.apache.marmotta.kiwi.sparql.builder.SQLBuilder; -import org.apache.marmotta.kiwi.sparql.builder.SQLVariable; +import org.apache.marmotta.kiwi.sparql.builder.model.SQLVariable; import org.apache.marmotta.kiwi.sparql.exception.UnsatisfiableQueryException; import org.openrdf.model.URI; import org.openrdf.model.impl.BNodeImpl;
