+ more comments
authorEric Prud'hommeaux <eric@w3.org>
Wed, 19 May 2010 21:02:04 +0200
changeset 194 9c8dfec36968
parent 193 4e5e879223fe
child 195 7cdd51c3b816
+ more comments
src/main/scala/SparqlToSql.scala
--- a/src/main/scala/SparqlToSql.scala	Wed May 19 15:23:18 2010 +0200
+++ b/src/main/scala/SparqlToSql.scala	Wed May 19 21:02:04 2010 +0200
@@ -704,6 +704,7 @@
    * Recursively add the joins, variable mappings and constraints for an SQL query implementing a graph pattern.
    * @param db  database description.
    * @return a new state including the subquery representing gp in a join.
+   * This is factored out of mapGraphPattern as it is used for MINUS and probably later for NOT EXISTS.
    */
   def synthesizeOuterJoin(initState:R2RState, gp:sparql.GraphPattern, negate:Boolean, db:sql.DatabaseDesc, enforceForeignKeys:Boolean):R2RState = {
     /** SPARQL OPTIONALs and UNIONs are treated as SQL subselects.
@@ -797,12 +798,18 @@
        * As { TP1, TP2 } === Join({ TP1 }, { TP2 }), we triple patterns and conjunctions all contribute to the same set of SQL joins, variable bindings and constraints.
        */
       case sparql.TriplesBlock(triplepatterns) => {
-	/* Examine each triple, updating the compilation state. */
+	/** Examine each triple, updating the compilation state. */
 	val state2 = triplepatterns.foldLeft(state)((incState,s) => bindOnPredicate(db, incState, s, enforceForeignKeys))
+
+	/** NULLs in the database result in no triple in the Direct Graph.
+	 * Enforce this by requiring that the SQL expression to which any SPARQL variable (Assignable) is bound is NOT NULL.
+	 */
 	val nullExprs = gp.findAssignables.foldLeft(Set[sql.Expression]())((s, vvar) => {
 	  if (varToAttributeDisjoints(state2.varmap, vvar).size == 0)
+	    /** Create a NOT NULL expression for each fully bound variable. */
 	    s ++ Set(sql.RelationalExpressionNotNull(sql.PrimaryExpressionAttr(varToAttribute(state2.varmap, vvar))))
 	  else
+	    /** Variables in a partial binding can be NULL so the aren't added to the null expressions. */
 	    s
 	})
 	R2RState(state2.joins, state2.varmap, state2.exprs ++ nullExprs)
@@ -826,6 +833,10 @@
 	 */
 	val state_postLeadingTable =
 	  if (state.joins.size == 0)
+	    /**
+	     * Leading optionals (ASK WHERE { OPTIONAL { ... } ... }) in SPARQL don't have a counterpart in SQL.
+	     * We emulate leading optionals with a leading SQL table which projects one solution with no selected attributes.
+	     */
 	    R2RState(state.joins + sql.InnerJoin(sql.AliasedResource(sql.Subselect(
 	      sql.Select(
 		sql.AttributeList(Set(sql.NamedAttribute(sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("1")),
@@ -835,6 +846,7 @@
 	      )), sql.RelVar(sql.Name("_EMPTY_"))), None), state.varmap, state.exprs)
 	  else
 	    state
+	/** Create an OUTER JOIN for the nested graph pattern. */
 	synthesizeOuterJoin(state_postLeadingTable, gp2, false, db, enforceForeignKeys)
       }
 
@@ -858,17 +870,17 @@
        * @param disjoinits  list of graph patterns to concatenate.
        */
       case sparql.TableDisjunction(disjoints) => {
-	/** SPARQL UNIONs are treated as SQL subselects.
+	/** SPARQL UNIONs are realized as SQL subselects.
 	 * Set up initial state for this subselect.
 	 */
-	val unionAlias = sql.RelVar(sql.Name("G_union" + state.joins.size))
-	val emptyState = R2RState(
+	val unionAlias = sql.RelVar(sql.Name("G_union" + state.joins.size)) // invent a unique name for this union.
+	val emptyState = R2RState( // create an empty state.
 	  util.AddOrderedSet[sql.Join](), 
 	  Map[sparql.Assignable, SQL2RDFValueMapper](), 
 	  Set[sql.Expression]()
 	)
 	val unionVars = disjoints.foldLeft(Set[sparql.Var]())((mySet,disjoint) =>
-	  mySet ++ disjoint.findVars).toList
+	  mySet ++ disjoint.findVars).toList // all variables nested in the disjoints.
 
 	/** Map the disjoints to subselects.
 	 * <no> is used for uniquely naming flags in the SELECTs used to