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;

Reply via email to