--- a/src/main/scala/SparqlToSql.scala Thu May 13 14:36:55 2010 -0400
+++ b/src/main/scala/SparqlToSql.scala Tue May 18 09:48:46 2010 +0200
@@ -79,28 +79,23 @@
* @param binding previous binding
* @param relVarAttr SQL relvar attribute to be bound, e.g. <code>G_union1.who</code>
* @param expr expr binding constraint, e.g. <code>G_opt6._DISJOINT_ IS NULL</code> or <code>G_union1._DISJOINT_!=0</code>
+ * @return PartialBinding on old constraints plus expr=relVarAttr
*/
def addExpr(binding:FullOrPartialBinding, relVarAttr:sql.RelVarAttr, expr:sql.RelationalExpression):FullOrPartialBinding = {
binding match {
case FullBinding(relvarattr) =>
- binding
+ error("Unexpected FullBinding to " + relvarattr + "\n" +
+ "because subselectVars should only find a pre-existing PartialBindings.")
+ // return binding if this codepath is needed later.
case PartialBinding(binders) =>
+ /**
+ * Add a new BindingConstraint to existing one.
+ * e.g. existing constraint: G_union1._DISJOINT_!=0,G_union1.name
+ * add for second side of union: G_union1._DISJOINT_!=1,G_union1.name
+ */
PartialBinding(binders + BindingConstraint(expr, relVarAttr))
}
}
- def toConstraint999(constrainMe:sql.RelVarAttr, against:FullOrPartialBinding):sql.Expression = {
- against match {
- case FullBinding(relvarattr) =>
- sql.RelationalExpressionEq(sql.PrimaryExpressionAttr(constrainMe),
- sql.PrimaryExpressionAttr(relvarattr))
- case PartialBinding(binders) =>
- sql.ExprConjunction({binders.map(b => {
- val BindingConstraint(expr, relvarattr) = b
- sql.ExprDisjunction(Set(expr,
- sql.RelationalExpressionEq(sql.PrimaryExpressionAttr(constrainMe),
- sql.PrimaryExpressionAttr(relvarattr))))})})
- }
- }
/**
* SQL terms representing SPARQL variables and bnodes.
@@ -557,6 +552,15 @@
}
+ /**
+ * return an SQL PrimaryExpression for rTerm.
+ *
+ * @param varmap map from variable to SQL2RDFValueMapper
+ * @param l an SQL relvarattr for a SPARQL Assignable (variable or bnode)
+ * @param rTerm SPARQL term to be converted to a PrimaryExpression
+ * @param sqlexpr an unbound SQL relational expression, e.g. sql.RelationalExpressionEq(_,_)
+ * @return an SQL expression on the relvarattrs to which the variables in f are bound
+ */
def assignable2expr(varmap:Map[sparql.Assignable, SQL2RDFValueMapper], l:sql.RelVarAttr, rTerm:sparql.Term, sqlexpr:(sql.PrimaryExpression, sql.PrimaryExpression) => sql.RelationalExpression):sql.RelationalExpression = { // :sparql.Var
val r:sql.PrimaryExpression = rTerm match {
case sparql.TermUri(u) => error("not implemented: translating RDF URI to SQL: " + u) // :sparql.Uri
@@ -575,68 +579,112 @@
sqlexpr(sql.PrimaryExpressionAttr(l), r)
}
+ /**
+ * create an SQL expression analogous to a SPARQL expression. The variables in
+ * f are expressed as the relvarattrs to which they are mapped. This function
+ * is only minimally implemented here.
+ *
+ * @param varmap map from variable to SQL2RDFValueMapper
+ * @param f SPARQL Expression
+ * @return an SQL expression on the relvarattrs to which the variables in f are bound
+ */
def filter2expr(varmap:Map[sparql.Assignable, SQL2RDFValueMapper], f:sparql.PrimaryExpression):sql.RelationalExpression = {
- val (lTerm:sparql.Term, rTerm:sparql.Term, sqlexpr) = f match { // sqlexpr::((sql.RelVarAttr,sql.PrimaryExpressionAttr)=>sql.RelationalExpression)
+
+ /** sqlexpr: an unbound RelationalExpression, i.e. a function which takes
+ * (sql.RelVarAttr, sql.PrimaryExpressionAttr) and returns an
+ * sql.RelationalExpression
+ */
+ val (lTerm:sparql.Term, rTerm:sparql.Term, sqlexpr) = f match {
case sparql.PrimaryExpressionEq(l, r) => (l.term, r.term, sql.RelationalExpressionEq(_,_))
case sparql.PrimaryExpressionLt(l, r) => (l.term, r.term, sql.RelationalExpressionLt(_,_))
}
-// sql.RelationalExpressionEq(_,_) === (x,y) => PrymaryExpressionEq(x,y)
+
lTerm match {
- // does not handle FILTER (<x> = ?v)
+ /** does not handle FILTER (<x> = ?v) */
case sparql.TermUri(obj) => error("only SPARQL PrimaryExpressions with a variable on the left have been implemented: punting on " + f)
- // FILTER (?v = <x> && ?v = ?x && ?v = 7)
+ /** FILTER (?v = <x> && ?v = ?x && ?v = 7) */
case sparql.TermVar(v) => assignable2expr(varmap, varToAttribute(varmap, sparql.VarAssignable(v)), rTerm, sqlexpr)
case sparql.TermBNode(b) => assignable2expr(varmap, varToAttribute(varmap, sparql.BNodeAssignable(b)), rTerm, sqlexpr)
- // does not handle FILTER (7 = ?v)
+ /** does not handle FILTER (7 = ?v) */
case sparql.TermLit(lit) => error("only SPARQL PrimaryExpressions with a variable on the left have been implemented: punting on " + f)
}
}
- /* subselectVars: Promote variables in OPTIONAL or UNION subselects to the
- * outer varmap/expressions.
- * <outerState> could be <myState> -- spliting roles could make proofs easier?
+ /**
+ * Promote a variable in an OPTIONAL or UNION subselect to the outer
+ * varmap/expressions.
*/
- def subselectVars(myState:R2RState, v:sparql.Assignable, optionalAlias:sql.RelVar,
+ def subselectVars(startState:R2RState, v:sparql.Assignable, optionalAlias:sql.RelVar,
optionalCond:sql.RelationalExpression,
outerVarmap:Map[sparql.Assignable, SQL2RDFValueMapper],
nestedVarmap:Map[sparql.Assignable, SQL2RDFValueMapper],
isOpt:Boolean):R2RState = {
+
val varAliasAttr = sql.RelVarAttr(optionalAlias, sql.Attribute(attrAliasNameFromVar(v)))
- if (myState.varmap.contains(v)) {
- /* The variable has already been bound. */
- val newMap:Map[sparql.Assignable, SQL2RDFValueMapper] = if (varToAttribute(myState.varmap, v) == varAliasAttr) {
- /* Same var was bound earlier. */
- Map(v -> { myState.varmap(v) match {
- case IntMapper(binding) => IntMapper(addExpr(binding, varAliasAttr, optionalCond))
- case StringMapper(binding) => StringMapper(addExpr(binding, varAliasAttr, optionalCond))
- case DateMapper(binding) => DateMapper(addExpr(binding, varAliasAttr, optionalCond))
- case RDFNoder(rel, binding) => RDFNoder(rel, addExpr(binding, varAliasAttr, optionalCond))
- case RDFBNoder(rel, binding) => RDFBNoder(rel, addExpr(binding, varAliasAttr, optionalCond))
- } } )
- } else
- Map()
+ if (startState.varmap.contains(v)) {
+
+ /** The variable has already been bound. */
+ val newMap:Map[sparql.Assignable, SQL2RDFValueMapper] =
+ /** If var was already bound to the same relvarattr... */
+ if (varToAttribute(startState.varmap, v) == varAliasAttr) {
+ /**
+ * Add new partial constraint to old partial constraints to produce a new binding.
+ * example:
+ * ?name -> StringMapper(PartialBinding(Set(BindingConstraint(G_union1._DISJOINT_!=0,G_union1.name),
+ * BindingConstraint(G_union1._DISJOINT_!=1,G_union1.name))))
+ */
+ Map(v -> {
+ startState.varmap(v) match {
+ case IntMapper(binding) => IntMapper(addExpr(binding, varAliasAttr, optionalCond))
+ case StringMapper(binding) => StringMapper(addExpr(binding, varAliasAttr, optionalCond))
+ case DateMapper(binding) => DateMapper(addExpr(binding, varAliasAttr, optionalCond))
+ case RDFNoder(rel, binding) => RDFNoder(rel, addExpr(binding, varAliasAttr, optionalCond))
+ case RDFBNoder(rel, binding) => RDFBNoder(rel, addExpr(binding, varAliasAttr, optionalCond))
+ } } )
+ } else {
+ /** Var was bound to a different relvarattr so handle as newConstraint below. */
+ Map()
+ }
+
val newConstraints =
- if (varToAttribute(outerVarmap, v) != varAliasAttr) {
- /* Constraint against binding from earlier GP. */
+ if (varToAttribute(outerVarmap, v) == varAliasAttr) { // also varToAttribute(startState.varmap, v) == varAliasAttr
+ Set()
+ } else {
+ /** Constraint against binding from earlier graph pattern.
+ * e.g. G_union1.who=R_who.empid */
val constraint = sql.RelationalExpressionEq(sql.PrimaryExpressionAttr(varAliasAttr),
sql.PrimaryExpressionAttr(varToAttribute(outerVarmap, v)))
- if (varToAttributeDisjoints(outerVarmap, v).size > 0)
- // (union0._DISJOINT_ != 0 OR union0.x=union1.x) AND (union1._DISJOINT_ != 2 OR union0.x=union1.x)
+ if (varToAttributeDisjoints(outerVarmap, v).size > 0) {
+ /**
+ * Construct a conjunction of disjunctions like:
+ * (union0._DISJOINT_ != 0 OR union0.x=union1.x) AND (union1._DISJOINT_ != 2 OR union0.x=union1.x)
+ */
varToAttributeDisjoints(outerVarmap, v) map ((d) =>
sql.ExprDisjunction({
- if (isOpt) Set(d, constraint)
- else Set(sql.ExprConjunction(Set(d, optionalCond)), constraint)
+ if (isOpt) Set(d, constraint) // a disjunction like: G_opt1._DISJOINT_ IS NULL OR G_opt3.birthday=G_opt1.birthday
+ else Set(sql.ExprConjunction(Set(d, optionalCond)), constraint) // @@ untested code path
}))
- else {
- if (isOpt) Set(constraint)
- else Set(sql.ExprDisjunction(Set(optionalCond, constraint)))
- }
- } else {
- Set()
+ } else {
+ if (isOpt)
+ /** just the constraint from above */
+ Set(constraint)
+ else
+ /**
+ * Above constraint applied only for this path:
+ * for example: (G_union1._DISJOINT_!=0) OR (G_union1.who=R_who.empid)
+ */
+ Set(sql.ExprDisjunction(Set(optionalCond, constraint)))
+ }
}
- R2RState(myState.joins, myState.varmap ++ newMap, myState.exprs ++ newConstraints)
+
+ R2RState(startState.joins, startState.varmap ++ newMap, startState.exprs ++ newConstraints)
} else {
- /* This variable is new to the outer context. */
+ /**
+ * This variable is new to the outer context so synthesize a new partial
+ * binding à la:
+ * ?name ->
+ * StringMapper(PartialBinding((G_union1._DISJOINT_!=0,G_union1.name)))
+ */
val p = PartialBinding(Set(BindingConstraint(optionalCond, varAliasAttr)))
val mapper:SQL2RDFValueMapper = nestedVarmap(v) match {
case IntMapper(_) => IntMapper(p)
@@ -645,12 +693,13 @@
case RDFNoder(rel, _) => RDFNoder(rel, p)
case RDFBNoder(rel, _) => RDFBNoder(rel, p)
}
- R2RState(myState.joins, myState.varmap + (v -> mapper), myState.exprs)
+
+ R2RState(startState.joins, startState.varmap + (v -> mapper), startState.exprs)
}
}
def synthesizeOuterJoin(initState:R2RState, gp:sparql.GraphPattern, negate:Boolean, db:sql.DatabaseDesc, enforceForeignKeys:Boolean):R2RState = {
- /* SPARQL OPTIONALs are treated as SQL subselects.
+ /** SPARQL OPTIONALs and UNIONs are treated as SQL subselects.
* Set up initial state for this subselect.
*/
val leftJoinAlias = sql.RelVar(sql.Name("G_opt" + initState.joins.size))
@@ -661,7 +710,7 @@
Set[sql.Expression]()
)
- /* Create the select for the nested graph pattern.
+ /** Create the select for the nested graph pattern.
*/
val optionalState = mapGraphPattern(db, emptyState, gp, enforceForeignKeys)
val optionalVars = gp.findVars
@@ -681,13 +730,13 @@
}
)
- /* Create a condition to test if this OPTIONAL was matched (called
+ /** Create a condition to test if this OPTIONAL was matched (called
* _DISJOINT_ as OPTIONAL behaves pretty much like a disjunction).
*/
val optionalCond = sql.RelationalExpressionNull(sql.PrimaryExpressionAttr(
sql.RelVarAttr(leftJoinAlias, sql.Attribute(sql.Name("_DISJOINT_")))))
- /* Bind variables to the attributes projected from the subselect; handle
+ /** Bind variables to the attributes projected from the subselect; handle
* corefs (equivalence with earlier bindings).
*/
val outerState2 =
@@ -698,7 +747,7 @@
subselectVars(myState, sparql.VarAssignable(v), leftJoinAlias, optionalCond,
initState.varmap, optionalState.varmap, true))
- /* The final state includes the subselect as a join, the variables bound
+ /** 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.
*/