+ comments
authorEric Prud'hommeaux <eric@w3.org>
Thu, 07 Jan 2010 10:48:43 -0500
changeset 119 cea99f45463a
parent 118 0ea6cb8f981a
child 120 284c037d1728
+ comments
src/main/scala/RDB2RDFMain.scala
--- a/src/main/scala/RDB2RDFMain.scala	Thu Jan 07 09:36:05 2010 -0500
+++ b/src/main/scala/RDB2RDFMain.scala	Thu Jan 07 10:48:43 2010 -0500
@@ -324,6 +324,9 @@
 	list.foldLeft(state)((incState,s) => mapGraphPattern(db, incState, s, enforceForeignKeys))
       }
       case sparql.TableDisjunction(list) => {
+	/* SPARQL UNIONs are treated as SQL subselects.
+	 * Set up initial state for this subselect.
+	 */
 	val unionAlias = sql.RelAlias(sql.Name("R_union" + state.joins.size))
 	val initDisjoints:Set[sql.Select] = Set()
 	val emptyState = R2RState(
@@ -332,6 +335,11 @@
 	  Set[sql.Expression]()
 	)
 	val unionVars = list.foldLeft(Set[sparql.Var]())((mySet,disjoint) => mySet ++ findVars(disjoint)).toList
+
+	/* Iterate over the disjoints, accumulating a state and a select for each
+	 * disjoint. The count is used for uniquely naming flags in the SELECTs
+	 * used to indicate which disjoint produced a tuple.
+	 */
 	val (state2, disjoints, count) = list.foldLeft((state, initDisjoints, 0))((incPair,disjoint) => {
 	  val (outerState, outerDisjoints, no) = incPair
 	  val disjointState = mapGraphPattern(db, emptyState, disjoint, enforceForeignKeys)
@@ -402,16 +410,24 @@
 	R2RState(state.joins + sql.InnerJoin(sql.AliasedResource(subselect,unionAlias)), state2.varmap, state2.exprs)
       }
       case sparql.OptionalGraphPattern(gp) => {
-	val state_postLeadingTable = if (state.joins.size == 0) {
-	  val emptySubselect = sql.Select(
-	    sql.AttributeList(Set(sql.NamedAttribute(sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("1")), sql.AttrAlias(sql.Name("_EMPTY_"))))),
-	    sql.TableList(util.AddOrderedSet()),
-	    None
-	  )
-	  R2RState(state.joins + sql.InnerJoin(sql.AliasedResource(sql.Subselect(emptySubselect),sql.RelAlias(sql.Name("_EMPTY_")))), state.varmap, state.exprs)
-	} else {
-	  state
-	}
+	/* state_postLeadingTable: create an initial table if the first conjoint is optional.
+	 * e.g. ... FROM (SELECT 1 AS _EMPTY_) AS _EMPTY_ LEFT OUTER JOIN ...
+	 */
+	val state_postLeadingTable =
+	  if (state.joins.size == 0)
+	    R2RState(state.joins + sql.InnerJoin(sql.AliasedResource(sql.Subselect(
+	      sql.Select(
+		sql.AttributeList(Set(sql.NamedAttribute(sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("1")),
+							 sql.AttrAlias(sql.Name("_EMPTY_"))))),
+		sql.TableList(util.AddOrderedSet()),
+		None
+	      )), sql.RelAlias(sql.Name("_EMPTY_")))), state.varmap, state.exprs)
+	  else
+	    state
+
+	/* SPARQL OPTIONALs are treated as SQL subselects.
+	 * Set up initial state for this subselect.
+	 */
       	val leftJoinAlias = sql.RelAlias(sql.Name("R_opt" + state_postLeadingTable.joins.size))
       	val initDisjoints:Set[sql.Select] = Set()
       	val emptyState = R2RState(
@@ -419,15 +435,16 @@
       	  Map[sparql.Var, SQL2RDFValueMapper](), 
       	  Set[sql.Expression]()
       	)
+
+	/* Create the select for the nested graph pattern.
+	 */
       	val optionalState = mapGraphPattern(db, emptyState, gp, enforceForeignKeys)
       	val optionalVars = findVars(gp)
 	val disjointNo = sql.NamedAttribute(sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("" + state_postLeadingTable.joins.size)), sql.AttrAlias(sql.Name("_DISJOINT_")))
-
       	val leftJoinVars = findVars(gp).toList
       	val attrlist:Set[sql.NamedAttribute] = leftJoinVars.foldLeft(Set(disjointNo))((attrs, v) =>
       	  attrs ++ Set(sql.NamedAttribute(varToAttribute(optionalState.varmap, v), sql.AttrAlias(sql.Name("A_" + v.s))))
       	)
-
       	val subselect = sql.Select(
       	  sql.AttributeList(attrlist),
       	  sql.TableList(optionalState.joins),
@@ -438,8 +455,14 @@
       	  }
       	)
 
-	val optionalNoAliasAttr = sql.RelAliasAttribute(leftJoinAlias, sql.Attribute(sql.Name("_DISJOINT_")))
-	val optionalCond = sql.RelationalExpressionNull(sql.PrimaryExpressionAttr(optionalNoAliasAttr))
+	/* Create a condition to test if this OPTIONAL was matched.
+	 */
+	val optionalCond = sql.RelationalExpressionNull(sql.PrimaryExpressionAttr(
+	  sql.RelAliasAttribute(leftJoinAlias, sql.Attribute(sql.Name("_DISJOINT_")))))
+
+	/* Bind variables to the attributes projected from the subselect; handle
+	 * corefs (equivalence with earlier bindings).
+	 */
 	val outerState2 = optionalVars.foldLeft(R2RState(state_postLeadingTable.joins, state_postLeadingTable.varmap, Set[sql.Expression]()))((myState, v) => {
 	  val varAliasAttr = sql.RelAliasAttribute(leftJoinAlias, sql.Attribute(sql.Name("A_" + v.s)))
 	  if (myState.varmap.contains(v)) {
@@ -479,17 +502,27 @@
 	    R2RState(myState.joins, myState.varmap + (v -> mapper), myState.exprs)
 	  }
 	})
+
+	/* The final state includes the subselect as a join, the variables bound
+	 * to subselect projection, and no new expresssions. The expressions
+	 * derived from corefs are conditions for the LEFT OUTER JOIN.
+	 */
       	val join = sql.LeftOuterJoin(sql.AliasedResource(sql.Subselect(subselect), leftJoinAlias), 
       	  outerState2.exprs.size match {
-      	    case 0 => // error ("Nested GP has no variables shared with its context; cowaredly refusing to join ON 1.")
-	      sql.RelationalExpressionEq(sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("1")), sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("1")))
+      	    case 0 =>
+	      /* Require corefs unless we have a leading OPTIONAL. */
+	      if (state.joins.size == 0)
+		sql.RelationalExpressionEq(sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("1")),
+					   sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("1")))
+	      else
+		error ("Nested GP has no variables shared with its context; cowaredly refusing to join ON 1.")
       	    case 1 => outerState2.exprs.toList(0)
       	    case _ => sql.ExprConjunction(outerState2.exprs)
       	  }
 			       )
-      	R2RState(state_postLeadingTable.joins + join, outerState2.varmap, state_postLeadingTable.exprs)
+      	R2RState(state_postLeadingTable.joins + join, outerState2.varmap, state.exprs)
       }
-      case x => error("no code to handle " + x)
+      case sparql.GraphGraphPattern(gp) => error("no code to handle GraphGraphPatterns (" + gp + ")")
     }
   }