You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@marmotta.apache.org by ss...@apache.org on 2014/01/14 11:31:31 UTC

git commit: cleanups in SPARQL module

Updated Branches:
  refs/heads/develop 967c1e239 -> 0aeedd8ab


cleanups in SPARQL module


Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo
Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/0aeedd8a
Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/0aeedd8a
Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/0aeedd8a

Branch: refs/heads/develop
Commit: 0aeedd8aba0d716343169621ae9151b3ce7a4b0f
Parents: 967c1e2
Author: Sebastian Schaffert <ss...@apache.org>
Authored: Tue Jan 14 11:31:19 2014 +0100
Committer: Sebastian Schaffert <ss...@apache.org>
Committed: Tue Jan 14 11:31:19 2014 +0100

----------------------------------------------------------------------
 .../kiwi/sparql/persistence/DistinctFinder.java |  47 ++++++
 .../sparql/persistence/FilterCollector.java     |  47 ++++++
 .../persistence/KiWiSparqlConnection.java       | 161 +-----------------
 .../kiwi/sparql/persistence/LimitFinder.java    |  44 +++++
 .../kiwi/sparql/persistence/OPTypeFinder.java   | 162 +++++++++++++++++++
 .../kiwi/sparql/persistence/OPTypes.java        |  27 ++++
 .../sparql/persistence/PatternCollector.java    |  46 ++++++
 .../kiwi/test/junit/KiWiDatabaseRunner.java     |   8 +-
 8 files changed, 377 insertions(+), 165 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/marmotta/blob/0aeedd8a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/DistinctFinder.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/DistinctFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/DistinctFinder.java
new file mode 100644
index 0000000..987ba69
--- /dev/null
+++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/DistinctFinder.java
@@ -0,0 +1,47 @@
+/*
+ * 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.persistence;
+
+import org.openrdf.query.algebra.Distinct;
+import org.openrdf.query.algebra.Reduced;
+import org.openrdf.query.algebra.TupleExpr;
+import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
+
+/**
+* Find distinct/reduced in a tuple expression.
+*
+* @author Sebastian Schaffert (sschaffert@apache.org)
+*/
+class DistinctFinder extends QueryModelVisitorBase<RuntimeException> {
+
+    boolean distinct = false;
+
+    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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/0aeedd8a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/FilterCollector.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/FilterCollector.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/FilterCollector.java
new file mode 100644
index 0000000..dbf33e1
--- /dev/null
+++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/FilterCollector.java
@@ -0,0 +1,47 @@
+/*
+ * 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.persistence;
+
+import org.openrdf.query.algebra.Filter;
+import org.openrdf.query.algebra.TupleExpr;
+import org.openrdf.query.algebra.ValueExpr;
+import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* Collect all filter conditions in a query expression
+*
+* @author Sebastian Schaffert (sschaffert@apache.org)
+*/
+class FilterCollector extends QueryModelVisitorBase<RuntimeException> {
+
+    List<ValueExpr> filters = new ArrayList<>();
+
+    FilterCollector(TupleExpr expr) {
+        expr.visit(this);
+    }
+
+    @Override
+    public void meet(Filter node) throws RuntimeException {
+        filters.add(node.getCondition());
+
+        super.meet(node);
+    }
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/0aeedd8a/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 8b91ab4..9e23adb 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
@@ -23,7 +23,6 @@ import info.aduna.iteration.CloseableIteratorIteration;
 import info.aduna.iteration.EmptyIteration;
 import info.aduna.iteration.Iterations;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.marmotta.commons.sesame.model.Namespaces;
 import org.apache.marmotta.commons.util.DateUtils;
 import org.apache.marmotta.kiwi.model.rdf.KiWiNode;
 import org.apache.marmotta.kiwi.persistence.KiWiConnection;
@@ -40,7 +39,6 @@ import org.openrdf.query.Binding;
 import org.openrdf.query.BindingSet;
 import org.openrdf.query.Dataset;
 import org.openrdf.query.algebra.*;
-import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
 import org.openrdf.query.impl.MapBindingSet;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -510,13 +508,13 @@ public class KiWiSparqlConnection {
         } else if(expr instanceof Compare) {
             Compare cmp = (Compare)expr;
 
-            OPTypes ot = determineOpType(cmp.getLeftArg(), cmp.getRightArg());
+            OPTypes ot = new OPTypeFinder(cmp).coerce();
 
             return evaluateExpression(cmp.getLeftArg(),queryVariables, ot) + getSQLOperator(cmp.getOperator()) + evaluateExpression(cmp.getRightArg(),queryVariables, ot);
         } else if(expr instanceof MathExpr) {
             MathExpr cmp = (MathExpr)expr;
 
-            OPTypes ot = determineOpType(cmp.getLeftArg(), cmp.getRightArg());
+            OPTypes ot = new OPTypeFinder(cmp).coerce();
 
             if(ot == OPTypes.STRING) {
                 if(cmp.getOperator() == MathExpr.MathOp.PLUS) {
@@ -742,88 +740,6 @@ public class KiWiSparqlConnection {
     }
 
 
-
-    private OPTypes determineOpType(ValueExpr expr) {
-        if(expr instanceof ValueConstant) {
-            if(((ValueConstant) expr).getValue() instanceof Literal) {
-                Literal l = (Literal)((ValueConstant) expr).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)) {
-                    return 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)) {
-                    return 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)) {
-                    return OPTypes.DATE;
-                } else {
-                    return OPTypes.STRING;
-                }
-            } else {
-                return OPTypes.STRING;
-            }
-        } else if(expr instanceof Str) {
-            return OPTypes.STRING;
-        } else if(expr instanceof Lang) {
-            return OPTypes.STRING;
-        } else if(expr instanceof LocalName) {
-            return OPTypes.STRING;
-        } else if(expr instanceof Label) {
-            return OPTypes.STRING;
-        } else if(expr instanceof MathExpr) {
-            return determineOpType(((MathExpr) expr).getLeftArg(), ((MathExpr) expr).getRightArg());
-        } else if(expr instanceof Var) {
-            return OPTypes.ANY;
-        } else if(expr instanceof FunctionCall) {
-            FunctionCall fc = (FunctionCall)expr;
-            URI fnUri = new URIImpl(fc.getURI());
-
-            String[] args = new String[fc.getArgs().size()];
-
-            OPTypes fOpType = functionReturnTypes.get(fnUri);
-            if(fOpType == null) {
-                fOpType = OPTypes.ANY;
-            }
-            return fOpType;
-        } else {
-            throw new IllegalArgumentException("unsupported expression: "+expr);
-        }
-    }
-
-    private OPTypes determineOpType(ValueExpr expr1, ValueExpr expr2) {
-        OPTypes left  = determineOpType(expr1);
-        OPTypes right = determineOpType(expr2);
-
-        if(left == OPTypes.ANY) {
-            return right;
-        } else if(right == OPTypes.ANY) {
-            return left;
-        } else if(left == right) {
-            return left;
-        } else if( (left == OPTypes.INT && right == OPTypes.DOUBLE) || (left == OPTypes.DOUBLE && right == OPTypes.INT)) {
-            return OPTypes.DOUBLE;
-        } else if( (left == OPTypes.STRING) || (right == OPTypes.STRING)) {
-            return OPTypes.STRING;
-        } else {
-            throw new IllegalArgumentException("unsupported type coercion: " + left + " and " + right);
-        }
-    }
-
-
     /**
      * Test if the regular expression given in the pattern can be simplified to a LIKE SQL statement; these are
      * considerably more efficient to evaluate in most databases, so in case we can simplify, we return a LIKE.
@@ -908,10 +824,6 @@ public class KiWiSparqlConnection {
     }
 
 
-    private static enum OPTypes {
-        STRING, DOUBLE, INT, DATE, BOOL, ANY
-    }
-
     public KiWiDialect getDialect() {
         return parent.getDialect();
     }
@@ -960,73 +872,4 @@ public class KiWiSparqlConnection {
     }
 
 
-    private static class LimitFinder extends QueryModelVisitorBase<RuntimeException> {
-
-        long limit = -1, offset = -1;
-
-        private 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();
-        }
-    }
-
-    private static class DistinctFinder extends QueryModelVisitorBase<RuntimeException> {
-
-        boolean distinct = false;
-
-        private 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;
-        }
-    }
-
-
-    private static class PatternCollector extends QueryModelVisitorBase<RuntimeException> {
-
-        List<StatementPattern> patterns = new ArrayList<>();
-
-        private PatternCollector(TupleExpr expr) {
-            expr.visit(this);
-        }
-
-        @Override
-        public void meet(StatementPattern node) throws RuntimeException {
-            patterns.add(node);
-
-            super.meet(node);
-        }
-    }
-
-
-    private static class FilterCollector extends QueryModelVisitorBase<RuntimeException> {
-
-        List<ValueExpr> filters = new ArrayList<>();
-
-        private FilterCollector(TupleExpr expr) {
-            expr.visit(this);
-        }
-
-        @Override
-        public void meet(Filter node) throws RuntimeException {
-            filters.add(node.getCondition());
-
-            super.meet(node);
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/marmotta/blob/0aeedd8a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/LimitFinder.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/LimitFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/LimitFinder.java
new file mode 100644
index 0000000..f0547c6
--- /dev/null
+++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/LimitFinder.java
@@ -0,0 +1,44 @@
+/*
+ * 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.persistence;
+
+import org.openrdf.query.algebra.Slice;
+import org.openrdf.query.algebra.TupleExpr;
+import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
+
+/**
+* Find the offset and limit values in a tuple expression
+*
+* @author Sebastian Schaffert (sschaffert@apache.org)
+*/
+class LimitFinder extends QueryModelVisitorBase<RuntimeException> {
+
+    long limit = -1, offset = -1;
+
+    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();
+    }
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/0aeedd8a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/OPTypeFinder.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/OPTypeFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/OPTypeFinder.java
new file mode 100644
index 0000000..85312f0
--- /dev/null
+++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/OPTypeFinder.java
@@ -0,0 +1,162 @@
+/*
+ * 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.persistence;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.marmotta.commons.sesame.model.Namespaces;
+import org.openrdf.model.Literal;
+import org.openrdf.model.URI;
+import org.openrdf.model.impl.URIImpl;
+import org.openrdf.model.vocabulary.FN;
+import org.openrdf.query.algebra.*;
+import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Determine the operand type of a value expression. Get the coerced value by calling coerce().
+ *
+ * @author Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class OPTypeFinder extends QueryModelVisitorBase<RuntimeException> {
+
+    List<OPTypes> optypes = new ArrayList<>();
+
+    private static Map<URI,OPTypes> functionReturnTypes = new HashMap<>();
+    static {
+        functionReturnTypes.put(FN.CONCAT, OPTypes.STRING);
+        functionReturnTypes.put(FN.CONTAINS, OPTypes.BOOL);
+        functionReturnTypes.put(FN.LOWER_CASE, OPTypes.STRING);
+        functionReturnTypes.put(FN.UPPER_CASE, OPTypes.STRING);
+        functionReturnTypes.put(FN.REPLACE, OPTypes.STRING);
+        functionReturnTypes.put(FN.SUBSTRING_AFTER, OPTypes.STRING);
+        functionReturnTypes.put(FN.SUBSTRING_BEFORE, OPTypes.STRING);
+        functionReturnTypes.put(FN.STARTS_WITH, OPTypes.BOOL);
+        functionReturnTypes.put(FN.ENDS_WITH, OPTypes.BOOL);
+        functionReturnTypes.put(FN.STRING_LENGTH, OPTypes.INT);
+        functionReturnTypes.put(FN.SUBSTRING, OPTypes.STRING);
+
+        functionReturnTypes.put(FN.NUMERIC_ABS, OPTypes.DOUBLE);
+        functionReturnTypes.put(FN.NUMERIC_CEIL, OPTypes.INT);
+        functionReturnTypes.put(FN.NUMERIC_FLOOR, OPTypes.INT);
+        functionReturnTypes.put(FN.NUMERIC_ROUND, OPTypes.INT);
+
+    }
+
+
+
+    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 {
+        URI fnUri = new URIImpl(fc.getURI());
+
+        String[] args = new String[fc.getArgs().size()];
+
+        OPTypes fOpType = functionReturnTypes.get(fnUri);
+        if(fOpType == null) {
+            fOpType = OPTypes.ANY;
+        }
+        optypes.add(fOpType);
+    }
+
+
+    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/0aeedd8a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/OPTypes.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/OPTypes.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/OPTypes.java
new file mode 100644
index 0000000..f1ab84b
--- /dev/null
+++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/OPTypes.java
@@ -0,0 +1,27 @@
+/*
+ * 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.persistence;
+
+/**
+* Operand types for operations - used for implicit type coercion.
+*
+* @author Sebastian Schaffert (sschaffert@apache.org)
+*/
+enum OPTypes {
+    STRING, DOUBLE, INT, DATE, BOOL, ANY
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/0aeedd8a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/PatternCollector.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/PatternCollector.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/PatternCollector.java
new file mode 100644
index 0000000..58331a4
--- /dev/null
+++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/PatternCollector.java
@@ -0,0 +1,46 @@
+/*
+ * 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.persistence;
+
+import org.openrdf.query.algebra.StatementPattern;
+import org.openrdf.query.algebra.TupleExpr;
+import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* Collect all statement patterns in a tuple expression.
+*
+* @author Sebastian Schaffert (sschaffert@apache.org)
+*/
+class PatternCollector extends QueryModelVisitorBase<RuntimeException> {
+
+    List<StatementPattern> patterns = new ArrayList<>();
+
+    PatternCollector(TupleExpr expr) {
+        expr.visit(this);
+    }
+
+    @Override
+    public void meet(StatementPattern node) throws RuntimeException {
+        patterns.add(node);
+
+        super.meet(node);
+    }
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/0aeedd8a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/KiWiDatabaseRunner.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/KiWiDatabaseRunner.java b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/KiWiDatabaseRunner.java
index 46d159d..25afcd9 100644
--- a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/KiWiDatabaseRunner.java
+++ b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/KiWiDatabaseRunner.java
@@ -38,11 +38,7 @@ import org.junit.runners.model.Statement;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.lang.annotation.Annotation;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import java.lang.annotation.*;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedList;
@@ -186,7 +182,7 @@ public class KiWiDatabaseRunner extends Suite {
             checkDB = new CheckDBRule(config);
             loggerRule = new ExecutionLogger();
         }
-        
+
         @Override
         protected void runChild(FrameworkMethod method, RunNotifier notifier) {
             final ForDialects forD = method.getAnnotation(ForDialects.class);