--- 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 + ")")
}
}