variable cleanups (proper description of variable mappings between SPARQL and SQL)
Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/25568a16 Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/25568a16 Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/25568a16 Branch: refs/heads/ldp Commit: 25568a16d4569256d79bbebc1603019773464d39 Parents: 64b504f Author: Sebastian Schaffert <[email protected]> Authored: Wed Sep 17 13:53:28 2014 +0200 Committer: Sebastian Schaffert <[email protected]> Committed: Wed Sep 17 13:53:28 2014 +0200 ---------------------------------------------------------------------- .../kiwi/sparql/builder/FunctionDescriptor.java | 85 ------ .../kiwi/sparql/builder/ProjectionType.java | 2 +- .../kiwi/sparql/builder/SQLBuilder.java | 275 ++++++++----------- .../kiwi/sparql/builder/SQLFunction.java | 85 ++++++ .../kiwi/sparql/builder/SQLVariable.java | 48 +++- .../persistence/KiWiSparqlConnection.java | 29 +- 6 files changed, 249 insertions(+), 275 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/marmotta/blob/25568a16/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/FunctionDescriptor.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/FunctionDescriptor.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/FunctionDescriptor.java deleted file mode 100644 index 8f81019..0000000 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/FunctionDescriptor.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.marmotta.kiwi.sparql.builder; - -import org.openrdf.model.URI; - -/** - * Description of a SPARQL function to give details about various characteristics that we need when translating to - * SQL, most importantly the parameter and return types. - * - * @author Sebastian Schaffert ([email protected]) - */ -public class FunctionDescriptor { - - public static enum Arity { ZERO, UNARY, BINARY, NARY }; - - private URI uri; - - private OPTypes parameterType, returnType; - - private Arity arity; - - public FunctionDescriptor(URI uri, OPTypes parameterType, OPTypes returnType, Arity arity) { - this.uri = uri; - this.parameterType = parameterType; - this.returnType = returnType; - this.arity = arity; - } - - - public URI getUri() { - return uri; - } - - public OPTypes getParameterType() { - return parameterType; - } - - public OPTypes getReturnType() { - return returnType; - } - - public Arity getArity() { - return arity; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - FunctionDescriptor that = (FunctionDescriptor) o; - - if (arity != that.arity) return false; - if (parameterType != that.parameterType) return false; - if (returnType != that.returnType) return false; - if (!uri.equals(that.uri)) return false; - - return true; - } - - @Override - public int hashCode() { - int result = uri.hashCode(); - result = 31 * result + parameterType.hashCode(); - result = 31 * result + returnType.hashCode(); - result = 31 * result + arity.hashCode(); - return result; - } -} http://git-wip-us.apache.org/repos/asf/marmotta/blob/25568a16/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/ProjectionType.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/ProjectionType.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/ProjectionType.java index 0c6c9fa..134b5c2 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/ProjectionType.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/ProjectionType.java @@ -23,5 +23,5 @@ package org.apache.marmotta.kiwi.sparql.builder; * @author Sebastian Schaffert ([email protected]) */ public enum ProjectionType { - NODE, URI, STRING, INT, DOUBLE, DATE + NODE, URI, STRING, INT, DOUBLE, DATE, NONE } http://git-wip-us.apache.org/repos/asf/marmotta/blob/25568a16/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java index 480cf0b..f2dc767 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java @@ -66,55 +66,55 @@ public class SQLBuilder { /** * Type coercion for function parameters and return values, defines for each function the type used by its parameters */ - private static Map<URI, FunctionDescriptor> functions = new HashMap<>(); + private static Map<URI, SQLFunction> functions = new HashMap<>(); - private static void addFunction(URI uri, OPTypes paramType, OPTypes returnType, FunctionDescriptor.Arity arity) { - functions.put(uri, new FunctionDescriptor(uri,paramType,returnType,arity)); + private static void addFunction(URI uri, OPTypes paramType, OPTypes returnType, SQLFunction.Arity arity) { + functions.put(uri, new SQLFunction(uri,paramType,returnType,arity)); } static { - addFunction(FN.CONCAT, OPTypes.STRING, OPTypes.STRING, FunctionDescriptor.Arity.NARY); - addFunction(FN.CONTAINS, OPTypes.STRING, OPTypes.BOOL, FunctionDescriptor.Arity.BINARY); - addFunction(FN.LOWER_CASE, OPTypes.STRING, OPTypes.STRING, FunctionDescriptor.Arity.UNARY); - addFunction(FN.UPPER_CASE, OPTypes.STRING, OPTypes.STRING, FunctionDescriptor.Arity.UNARY); - addFunction(FN.REPLACE, OPTypes.STRING, OPTypes.STRING, FunctionDescriptor.Arity.BINARY); - addFunction(FN.SUBSTRING_AFTER, OPTypes.STRING, OPTypes.STRING, FunctionDescriptor.Arity.BINARY); - addFunction(FN.SUBSTRING_BEFORE, OPTypes.STRING, OPTypes.STRING, FunctionDescriptor.Arity.BINARY); - addFunction(FN.STARTS_WITH, OPTypes.STRING, OPTypes.BOOL, FunctionDescriptor.Arity.BINARY); - addFunction(FN.ENDS_WITH, OPTypes.STRING, OPTypes.BOOL, FunctionDescriptor.Arity.BINARY); - addFunction(FN.STRING_LENGTH, OPTypes.STRING, OPTypes.INT, FunctionDescriptor.Arity.BINARY); - addFunction(FN.SUBSTRING, OPTypes.STRING, OPTypes.STRING, FunctionDescriptor.Arity.BINARY); - - addFunction(FN.NUMERIC_ABS, OPTypes.DOUBLE, OPTypes.DOUBLE, FunctionDescriptor.Arity.UNARY); - addFunction(FN.NUMERIC_CEIL, OPTypes.DOUBLE, OPTypes.INT, FunctionDescriptor.Arity.UNARY); - addFunction(FN.NUMERIC_FLOOR, OPTypes.DOUBLE, OPTypes.INT, FunctionDescriptor.Arity.UNARY); - addFunction(FN.NUMERIC_ROUND, OPTypes.DOUBLE, OPTypes.INT, FunctionDescriptor.Arity.UNARY); - - - addFunction(FN_MARMOTTA.YEAR, OPTypes.DATE, OPTypes.INT, FunctionDescriptor.Arity.UNARY); - addFunction(FN_MARMOTTA.MONTH, OPTypes.DATE, OPTypes.INT, FunctionDescriptor.Arity.UNARY); - addFunction(FN_MARMOTTA.DAY, OPTypes.DATE, OPTypes.INT, FunctionDescriptor.Arity.UNARY); - addFunction(FN_MARMOTTA.HOURS, OPTypes.DATE, OPTypes.INT, FunctionDescriptor.Arity.UNARY); - addFunction(FN_MARMOTTA.MINUTES, OPTypes.DATE, OPTypes.INT, FunctionDescriptor.Arity.UNARY); - addFunction(FN_MARMOTTA.SECONDS, OPTypes.DATE, OPTypes.INT, FunctionDescriptor.Arity.UNARY); - addFunction(FN_MARMOTTA.TIMEZONE, OPTypes.DATE, OPTypes.STRING, FunctionDescriptor.Arity.UNARY); - addFunction(FN_MARMOTTA.TZ, OPTypes.DATE, OPTypes.STRING, FunctionDescriptor.Arity.UNARY); - - addFunction(FN_MARMOTTA.MD5, OPTypes.STRING, OPTypes.STRING, FunctionDescriptor.Arity.UNARY); - addFunction(FN_MARMOTTA.SHA1, OPTypes.STRING, OPTypes.STRING, FunctionDescriptor.Arity.UNARY); - addFunction(FN_MARMOTTA.SHA256, OPTypes.STRING, OPTypes.STRING, FunctionDescriptor.Arity.UNARY); - addFunction(FN_MARMOTTA.SHA384, OPTypes.STRING, OPTypes.STRING, FunctionDescriptor.Arity.UNARY); - addFunction(FN_MARMOTTA.SHA512, OPTypes.STRING, OPTypes.STRING, FunctionDescriptor.Arity.UNARY); - - addFunction(FN_MARMOTTA.UUID, OPTypes.ANY, OPTypes.URI, FunctionDescriptor.Arity.ZERO); - addFunction(FN_MARMOTTA.STRUUID, OPTypes.ANY, OPTypes.STRING, FunctionDescriptor.Arity.ZERO); - addFunction(FN_MARMOTTA.RAND, OPTypes.ANY, OPTypes.DOUBLE, FunctionDescriptor.Arity.ZERO); - - addFunction(FN_MARMOTTA.STDDEV, OPTypes.DOUBLE, OPTypes.DOUBLE, FunctionDescriptor.Arity.UNARY); - addFunction(FN_MARMOTTA.VARIANCE, OPTypes.DOUBLE, OPTypes.DOUBLE, FunctionDescriptor.Arity.UNARY); - - addFunction(FN_MARMOTTA.QUERY_FULLTEXT, OPTypes.STRING, OPTypes.BOOL, FunctionDescriptor.Arity.NARY); - addFunction(FN_MARMOTTA.SEARCH_FULLTEXT, OPTypes.STRING, OPTypes.BOOL, FunctionDescriptor.Arity.NARY); + addFunction(FN.CONCAT, OPTypes.STRING, OPTypes.STRING, SQLFunction.Arity.NARY); + addFunction(FN.CONTAINS, OPTypes.STRING, OPTypes.BOOL, SQLFunction.Arity.BINARY); + addFunction(FN.LOWER_CASE, OPTypes.STRING, OPTypes.STRING, SQLFunction.Arity.UNARY); + addFunction(FN.UPPER_CASE, OPTypes.STRING, OPTypes.STRING, SQLFunction.Arity.UNARY); + addFunction(FN.REPLACE, OPTypes.STRING, OPTypes.STRING, SQLFunction.Arity.BINARY); + addFunction(FN.SUBSTRING_AFTER, OPTypes.STRING, OPTypes.STRING, SQLFunction.Arity.BINARY); + addFunction(FN.SUBSTRING_BEFORE, OPTypes.STRING, OPTypes.STRING, SQLFunction.Arity.BINARY); + addFunction(FN.STARTS_WITH, OPTypes.STRING, OPTypes.BOOL, SQLFunction.Arity.BINARY); + addFunction(FN.ENDS_WITH, OPTypes.STRING, OPTypes.BOOL, SQLFunction.Arity.BINARY); + addFunction(FN.STRING_LENGTH, OPTypes.STRING, OPTypes.INT, SQLFunction.Arity.BINARY); + addFunction(FN.SUBSTRING, OPTypes.STRING, OPTypes.STRING, SQLFunction.Arity.BINARY); + + addFunction(FN.NUMERIC_ABS, OPTypes.DOUBLE, OPTypes.DOUBLE, SQLFunction.Arity.UNARY); + addFunction(FN.NUMERIC_CEIL, OPTypes.DOUBLE, OPTypes.INT, SQLFunction.Arity.UNARY); + addFunction(FN.NUMERIC_FLOOR, OPTypes.DOUBLE, OPTypes.INT, SQLFunction.Arity.UNARY); + addFunction(FN.NUMERIC_ROUND, OPTypes.DOUBLE, OPTypes.INT, SQLFunction.Arity.UNARY); + + + addFunction(FN_MARMOTTA.YEAR, OPTypes.DATE, OPTypes.INT, SQLFunction.Arity.UNARY); + addFunction(FN_MARMOTTA.MONTH, OPTypes.DATE, OPTypes.INT, SQLFunction.Arity.UNARY); + addFunction(FN_MARMOTTA.DAY, OPTypes.DATE, OPTypes.INT, SQLFunction.Arity.UNARY); + addFunction(FN_MARMOTTA.HOURS, OPTypes.DATE, OPTypes.INT, SQLFunction.Arity.UNARY); + addFunction(FN_MARMOTTA.MINUTES, OPTypes.DATE, OPTypes.INT, SQLFunction.Arity.UNARY); + addFunction(FN_MARMOTTA.SECONDS, OPTypes.DATE, OPTypes.INT, SQLFunction.Arity.UNARY); + addFunction(FN_MARMOTTA.TIMEZONE, OPTypes.DATE, OPTypes.STRING, SQLFunction.Arity.UNARY); + addFunction(FN_MARMOTTA.TZ, OPTypes.DATE, OPTypes.STRING, SQLFunction.Arity.UNARY); + + addFunction(FN_MARMOTTA.MD5, OPTypes.STRING, OPTypes.STRING, SQLFunction.Arity.UNARY); + addFunction(FN_MARMOTTA.SHA1, OPTypes.STRING, OPTypes.STRING, SQLFunction.Arity.UNARY); + addFunction(FN_MARMOTTA.SHA256, OPTypes.STRING, OPTypes.STRING, SQLFunction.Arity.UNARY); + addFunction(FN_MARMOTTA.SHA384, OPTypes.STRING, OPTypes.STRING, SQLFunction.Arity.UNARY); + addFunction(FN_MARMOTTA.SHA512, OPTypes.STRING, OPTypes.STRING, SQLFunction.Arity.UNARY); + + addFunction(FN_MARMOTTA.UUID, OPTypes.ANY, OPTypes.URI, SQLFunction.Arity.ZERO); + addFunction(FN_MARMOTTA.STRUUID, OPTypes.ANY, OPTypes.STRING, SQLFunction.Arity.ZERO); + addFunction(FN_MARMOTTA.RAND, OPTypes.ANY, OPTypes.DOUBLE, SQLFunction.Arity.ZERO); + + addFunction(FN_MARMOTTA.STDDEV, OPTypes.DOUBLE, OPTypes.DOUBLE, SQLFunction.Arity.UNARY); + addFunction(FN_MARMOTTA.VARIANCE, OPTypes.DOUBLE, OPTypes.DOUBLE, SQLFunction.Arity.UNARY); + + addFunction(FN_MARMOTTA.QUERY_FULLTEXT, OPTypes.STRING, OPTypes.BOOL, SQLFunction.Arity.NARY); + addFunction(FN_MARMOTTA.SEARCH_FULLTEXT, OPTypes.STRING, OPTypes.BOOL, SQLFunction.Arity.NARY); } @@ -140,29 +140,15 @@ public class SQLBuilder { private Set<String> groupLabels; /** - * A map for mapping the SPARQL variable names to internal names used for constructing SQL aliases. - * Will look like { ?x -> "V1", ?y -> "V2", ... } + * Maintains a mapping between SPARQL variable names and internal variable descriptions. The internal description + * contains information whether the variable needs to be projected, what SQL expressions represent this variable, + * and what internal aliases to use. */ - private Map<Var,String> variableNames = new HashMap<>(); - - /** - * 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 Map<String,List<String>> queryVariables = new HashMap<>(); - - - /** - * A map for mapping SPARQL variables to database 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. - */ - private Map<Var,List<String>> queryVariableIds = new HashMap<>(); - - - private Map<Var, ProjectionType> variableTypes = new HashMap<>(); + private Map<String,SQLVariable> variables = new HashMap<>(); + private void addVariable(SQLVariable v) { + variables.put(v.getSparqlVariable().getName(),v); + } - private Set<Var> projectedVariables = new HashSet<>(); /** * The triple patterns collected from the query. @@ -212,56 +198,9 @@ public class SQLBuilder { prepareBuilder(); } - public boolean isDistinct() { - return distinct; - } - - public void setDistinct(boolean distinct) { - this.distinct = distinct; - } - - public long getOffset() { - return offset; - } - - public void setOffset(long offset) { - this.offset = offset; - } - - public long getLimit() { - return limit; - } - - public void setLimit(long limit) { - this.limit = limit; - } - - /** - * Return the set of variables projected by the SELECT clause - * @return - */ - public Set<Var> getProjectedVariables() { - return projectedVariables; - } - - /** - * Return the SQL name of the SPARQL variable given as argument as generated by this query builder. - * @param v - * @return - */ - public String getVariableName(Var v) { - return variableNames.get(v); - } - - /** - * Return the type of a projected variable. Usually this is a node id to be resolved in the next step, but some - * projections also create literal values of various types. - * @param v - * @return - */ - public ProjectionType getVariableType(Var v) { - return variableTypes.get(v); + public Map<String, SQLVariable> getVariables() { + return variables; } private void prepareBuilder() throws UnsatisfiableQueryException { @@ -302,29 +241,32 @@ public class SQLBuilder { for (int i = 0; i < fields.length; i++) { if (fields[i] != null && !fields[i].hasValue()) { Var v = fields[i]; - if (variableNames.get(v) == null) { - variableNames.put(v, "V" + (++variableCount)); - variableTypes.put(v, ProjectionType.NODE); - queryVariables.put(v.getName(), new LinkedList<String>()); - queryVariableIds.put(v, new LinkedList<String>()); + + SQLVariable sv = variables.get(v.getName()); + if(!variables.containsKey(v.getName())) { + sv = new SQLVariable("V" + (++variableCount), v); // select those variables that are really projected and not only needed in a grouping construct if(new SQLProjectionFinder(query,v).found) { - projectedVariables.add(v); + sv.setProjectionType(ProjectionType.NODE); } + + addVariable(sv); } + String pName = p.getName(); - String vName = variableNames.get(v); + String vName = sv.getName(); + if (hasNodeCondition(fields[i], query)) { - queryVariables.get(v.getName()).add(pName + "_" + positions[i] + "_" + vName); + sv.getAliases().add(pName + "_" + positions[i] + "_" + vName); } // if the variable has been used before, add a join condition to the first occurrence - if(queryVariableIds.get(v).size() > 0) { - p.getConditions().add(queryVariableIds.get(v).get(0) + " = " + pName + "." + positions[i]); + if(sv.getExpressions().size() > 0) { + p.getConditions().add(sv.getExpressions().get(0) + " = " + pName + "." + positions[i]); } - queryVariableIds.get(v).add(pName + "." + positions[i]); + sv.getExpressions().add(pName + "." + positions[i]); } } } @@ -335,23 +277,23 @@ public class SQLBuilder { // add all extensions to the variable list so they are properly considered in projections and clauses for(ExtensionElem ext : extensions) { Var v = new Var(ext.getName()); - if (variableNames.get(v) == null) { - variableNames.put(v, "V" + (++variableCount)); - // set variable type according to expression; we need this later to decide what kind of node to construct - variableTypes.put(v, getProjectionType(ext.getExpr())); - queryVariables.put(v.getName(), new LinkedList<String>()); - queryVariableIds.put(v, new LinkedList<String>()); + SQLVariable sv = variables.get(v.getName()); + if(!variables.containsKey(v.getName())) { + sv = new SQLVariable("V" + (++variableCount), v); // select those variables that are really projected and not only needed in a grouping construct if(new SQLProjectionFinder(query,v).found) { - projectedVariables.add(v); + sv.setProjectionType(getProjectionType(ext.getExpr())); } + + addVariable(sv); } + if (hasNodeCondition(v, query)) { - queryVariables.get(v.getName()).add(evaluateExpression(ext.getExpr(), OPTypes.ANY)); + sv.getAliases().add(evaluateExpression(ext.getExpr(), OPTypes.ANY)); } - queryVariableIds.get(v).add(evaluateExpression(ext.getExpr(), OPTypes.ANY)); + sv.getExpressions().add(evaluateExpression(ext.getExpr(), OPTypes.ANY)); } @@ -519,7 +461,7 @@ public class SQLBuilder { for (SQLPattern p : f.getPatterns()) { for(Map.Entry<SQLPattern.TripleColumns, Var> fieldEntry : p.getTripleFields().entrySet()) { if(fieldEntry.getValue() != null && !fieldEntry.getValue().hasValue() && hasNodeCondition(fieldEntry.getValue(),query)) { - p.setJoinField(fieldEntry.getKey(), variableNames.get(fieldEntry.getValue())); + p.setJoinField(fieldEntry.getKey(), variables.get(fieldEntry.getValue().getName()).getName()); } } } @@ -530,17 +472,16 @@ public class SQLBuilder { private String buildSelectClause() { List<String> projections = new ArrayList<>(); - for(Iterator<Var> it = queryVariableIds.keySet().iterator(); it.hasNext(); ) { - Var v = it.next(); - if(projectedVariables.contains(v)) { - String projectedName = variableNames.get(v); - String fromName = queryVariableIds.get(v).get(0); + for(SQLVariable v : variables.values()) { + if(v.getProjectionType() != ProjectionType.NONE) { + String projectedName = v.getName(); + String fromName = v.getExpressions().get(0); projections.add(fromName + " AS " + projectedName); - } } + StringBuilder selectClause = new StringBuilder(); if(distinct) { @@ -597,18 +538,18 @@ public class SQLBuilder { // to the node given as binding if(bindings != null) { for(String v : bindings.getBindingNames()) { - for(Map.Entry<Var,List<String>> entry : queryVariableIds.entrySet()) { - if(entry.getKey().getName() != null && entry.getKey().getName().equals(v) && - entry.getValue() != null && entry.getValue().size() > 0) { - List<String> vNames = entry.getValue(); - String vName = vNames.get(0); - Value binding = converter.convert(bindings.getValue(v)); - if(binding instanceof KiWiNode) { - whereConditions.add(vName+" = "+((KiWiNode)binding).getId()); - } else { - throw new UnsatisfiableQueryException("the values in this binding have not been created by the KiWi value factory"); - } + SQLVariable sv = variables.get(v); + + if(!sv.getExpressions().isEmpty()) { + List<String> vNames = sv.getExpressions(); + String vName = vNames.get(0); + Value binding = converter.convert(bindings.getValue(v)); + if(binding instanceof KiWiNode) { + whereConditions.add(vName+" = "+((KiWiNode)binding).getId()); + } else { + throw new UnsatisfiableQueryException("the values in this binding have not been created by the KiWi value factory"); } + } } } @@ -652,11 +593,10 @@ public class SQLBuilder { if(groupLabels.size() > 0) { groupClause.append("GROUP BY "); for(Iterator<String> it = groupLabels.iterator(); it.hasNext(); ) { - Var v = new Var(it.next()); - + SQLVariable sv = variables.get(it.next()); - if(queryVariableIds.get(v) != null) { - Iterator<String> lit = queryVariableIds.get(v).iterator(); + if(sv != null) { + Iterator<String> lit = sv.getExpressions().iterator(); while (lit.hasNext()) { groupClause.append(lit.next()); if(lit.hasNext()) { @@ -715,7 +655,7 @@ public class SQLBuilder { Lang lang = (Lang)expr; if(lang.getArg() instanceof Var) { - return queryVariables.get(((Var) lang.getArg()).getName()).get(0) + ".lang"; + return variables.get(((Var) lang.getArg()).getName()).getPrimaryAlias() + ".lang"; } } else if(expr instanceof Compare) { Compare cmp = (Compare)expr; @@ -760,7 +700,7 @@ public class SQLBuilder { if(arg instanceof ValueConstant) { return Boolean.toString(((ValueConstant) arg).getValue() instanceof URI || ((ValueConstant) arg).getValue() instanceof BNode); } else if(arg instanceof Var) { - String var = queryVariables.get(((Var) arg).getName()).get(0); + String var = variables.get(((Var) arg).getName()).getPrimaryAlias(); return "(" + var + ".ntype = 'uri' OR " + var + ".ntype = 'bnode')"; } @@ -771,7 +711,7 @@ public class SQLBuilder { if(arg instanceof ValueConstant) { return Boolean.toString(((ValueConstant) arg).getValue() instanceof URI); } else if(arg instanceof Var) { - String var = queryVariables.get(((Var) arg).getName()).get(0); + String var = variables.get(((Var) arg).getName()).getPrimaryAlias(); return var + ".ntype = 'uri'"; } @@ -782,7 +722,7 @@ public class SQLBuilder { if(arg instanceof ValueConstant) { return Boolean.toString(((ValueConstant) arg).getValue() instanceof BNode); } else if(arg instanceof Var) { - String var = queryVariables.get(((Var) arg).getName()).get(0); + String var = variables.get(((Var) arg).getName()).getPrimaryAlias(); return var + ".ntype = 'bnode'"; } @@ -793,12 +733,12 @@ public class SQLBuilder { if(arg instanceof ValueConstant) { return Boolean.toString(((ValueConstant) arg).getValue() instanceof Literal); } else if(arg instanceof Var) { - String var = queryVariables.get(((Var) arg).getName()).get(0); + String var = variables.get(((Var) arg).getName()).getPrimaryAlias(); return "(" + var + ".ntype = 'string' OR " + var + ".ntype = 'int' OR " + var + ".ntype = 'double' OR " + var + ".ntype = 'date' OR " + var + ".ntype = 'boolean')"; } } else if(expr instanceof Var) { - String var = queryVariables.get(((Var) expr).getName()).get(0); + String var = variables.get(((Var) expr).getName()).getPrimaryAlias(); if(optype == null) { return var + ".svalue"; @@ -851,9 +791,9 @@ public class SQLBuilder { // so instead we construct an ARRAY of the bindings of all variables List<String> countVariables = new ArrayList<>(); - for(Map.Entry<Var,List<String>> v : queryVariableIds.entrySet()) { - if(!projectedVariables.contains(v.getKey())) { - countVariables.add(v.getValue().get(0)); + for(SQLVariable v : variables.values()) { + if(v.getProjectionType() == ProjectionType.NONE) { + countVariables.add(v.getExpressions().get(0)); } } countExp.append("ARRAY["); @@ -1171,8 +1111,7 @@ public class SQLBuilder { log.debug("original SPARQL syntax tree:\n {}", query); log.debug("constructed SQL query string:\n {}",queryString); - log.debug("SPARQL -> SQL node variable mappings:\n {}", queryVariables); - log.debug("SPARQL -> SQL ID variable mappings:\n {}", queryVariableIds); + log.debug("SPARQL -> SQL node variable mappings:\n {}", variables); return queryString; } http://git-wip-us.apache.org/repos/asf/marmotta/blob/25568a16/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLFunction.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLFunction.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLFunction.java new file mode 100644 index 0000000..527da72 --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLFunction.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.marmotta.kiwi.sparql.builder; + +import org.openrdf.model.URI; + +/** + * Description of a SPARQL function to give details about various characteristics that we need when translating to + * SQL, most importantly the parameter and return types. + * + * @author Sebastian Schaffert ([email protected]) + */ +public class SQLFunction { + + public static enum Arity { ZERO, UNARY, BINARY, NARY }; + + private URI uri; + + private OPTypes parameterType, returnType; + + private Arity arity; + + public SQLFunction(URI uri, OPTypes parameterType, OPTypes returnType, Arity arity) { + this.uri = uri; + this.parameterType = parameterType; + this.returnType = returnType; + this.arity = arity; + } + + + public URI getUri() { + return uri; + } + + public OPTypes getParameterType() { + return parameterType; + } + + public OPTypes getReturnType() { + return returnType; + } + + public Arity getArity() { + return arity; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SQLFunction that = (SQLFunction) o; + + if (arity != that.arity) return false; + if (parameterType != that.parameterType) return false; + if (returnType != that.returnType) return false; + if (!uri.equals(that.uri)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = uri.hashCode(); + result = 31 * result + parameterType.hashCode(); + result = 31 * result + returnType.hashCode(); + result = 31 * result + arity.hashCode(); + return result; + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/25568a16/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java index 436558b..0950796 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java @@ -23,7 +23,8 @@ import java.util.ArrayList; import java.util.List; /** - * Representation of a SPARQL variable in SQL. + * 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]) */ @@ -46,18 +47,24 @@ public class SQLVariable { /** - * A map for mapping SPARQL variables to database 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. + * 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> nodeIds; + private List<String> expressions; + /** + * 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; public SQLVariable(String name, Var sparqlVariable) { this.name = name; this.sparqlVariable = sparqlVariable; this.aliases = new ArrayList<>(); - this.nodeIds = new ArrayList<>(); + this.expressions = new ArrayList<>(); } public String getName() { @@ -72,7 +79,34 @@ public class SQLVariable { return aliases; } - public List<String> getNodeIds() { - return nodeIds; + /** + * Primary alias for a variable, used e.g. when projecting or evaluating in functions. All others are added with join conditions. + * @return + */ + public String getPrimaryAlias() { + return aliases.get(0); + } + + public List<String> getExpressions() { + return expressions; + } + + public ProjectionType getProjectionType() { + return projectionType; + } + + public void setProjectionType(ProjectionType projectionType) { + this.projectionType = projectionType; + } + + @Override + public String toString() { + return "Variable{" + + "SQL name='" + name + '\'' + + ", SPARQL name=" + sparqlVariable.getName() + + ", aliases=" + aliases + + ", expressions=" + expressions + + ", projectionType=" + projectionType + + '}'; } } http://git-wip-us.apache.org/repos/asf/marmotta/blob/25568a16/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 a662e40..1bd008c 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,6 +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.exception.UnsatisfiableQueryException; import org.openrdf.model.impl.LiteralImpl; import org.openrdf.model.impl.URIImpl; @@ -37,7 +38,6 @@ import org.openrdf.query.Binding; import org.openrdf.query.BindingSet; import org.openrdf.query.Dataset; import org.openrdf.query.algebra.TupleExpr; -import org.openrdf.query.algebra.Var; import org.openrdf.query.impl.MapBindingSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -118,40 +118,41 @@ public class KiWiSparqlConnection { public BindingSet apply(ResultSet row) throws SQLException { MapBindingSet resultRow = new MapBindingSet(); - List<Var> vars = new ArrayList<>(builder.getProjectedVariables()); + List<SQLVariable> vars = new ArrayList<>(builder.getVariables().values()); long[] nodeIds = new long[vars.size()]; for(int i=0; i<vars.size(); i++) { - if(builder.getVariableType(vars.get(i)) == ProjectionType.NODE) { - nodeIds[i] = row.getLong(builder.getVariableName(vars.get(i))); + SQLVariable sv = vars.get(i); + if(sv.getProjectionType() == ProjectionType.NODE) { + nodeIds[i] = row.getLong(sv.getName()); } } KiWiNode[] nodes = parent.loadNodesByIds(nodeIds); for (int i = 0; i < vars.size(); i++) { - Var v = vars.get(i); + SQLVariable sv = vars.get(i); if(nodes[i] != null) { // resolved node - resultRow.addBinding(v.getName(), nodes[i]); - } else { + resultRow.addBinding(sv.getSparqlVariable().getName(), nodes[i]); + } else if(sv.getProjectionType() != ProjectionType.NONE) { // literal value - String value = row.getString(builder.getVariableName(v)); + String value = row.getString(sv.getName()); if(value != null) { - switch (builder.getVariableType(v)) { + switch (sv.getProjectionType()) { case URI: - resultRow.addBinding(v.getName(), new URIImpl(value)); + resultRow.addBinding(sv.getSparqlVariable().getName(), new URIImpl(value)); break; case INT: - resultRow.addBinding(v.getName(), new LiteralImpl(value, XSD.Integer)); + resultRow.addBinding(sv.getSparqlVariable().getName(), new LiteralImpl(value, XSD.Integer)); break; case DOUBLE: - resultRow.addBinding(v.getName(), new LiteralImpl(value, XSD.Double)); + resultRow.addBinding(sv.getSparqlVariable().getName(), new LiteralImpl(value, XSD.Double)); break; case STRING: - resultRow.addBinding(v.getName(), new LiteralImpl(value)); + resultRow.addBinding(sv.getSparqlVariable().getName(), new LiteralImpl(value)); break; default: - resultRow.addBinding(v.getName(), new LiteralImpl(value)); + resultRow.addBinding(sv.getSparqlVariable().getName(), new LiteralImpl(value)); break; } }
