--- a/src/main/scala/RDB2RDFMain.scala Thu Jan 07 11:09:08 2010 -0500
+++ b/src/main/scala/RDB2RDFMain.scala Thu Jan 07 12:32:15 2010 -0500
@@ -1,3 +1,12 @@
+/* RDB2RDF: convert SPARQL queries to sound SQL queries.
+ *
+ * Please read from the bottom -- i.e. apply calls mapGraphPattern with the root
+ * graph pattern. mapGraphPattern handles all the graph pattern types in SPARQL,
+ * effectively peforming the Convert Graph Patterns step in SPARQL 1.0 12.2.1
+ * <http://www.w3.org/TR/rdf-sparql-query/#convertGraphPattern>
+ * with the target semantics in SQL instead of SPARQL.
+ */
+
package w3c.sw.rdb2rdf
import scala.util.parsing.combinator._
@@ -67,16 +76,7 @@
}
/** varConstraint
- * called on triple pattern subjects and objects of type variable
- * passed the relation name (from predicate)
- * if a subject, then the attribute is the primary key for relation
- * if an object, then passed the attribute name (from predicate)
- * passed the relalias for this relation (e.g. _emp)
- *
- * schema(Employee.id) => (?emp => NodeTemplate("Employee", R_emp.id) stemURI + rel + fk(rel) + value
- * schema(Employee.lastName) => (?lastName => RValueString(_Remp.lastName)
- * schema(Employee.manater) => (?manager => ForeignKey("Employee", R_manager.id)
- *
+ * examples:
* SELECT ?emp WHERE { ?emp emp:manager <http://hr.example/our/favorite/DB/Employee/id.18#record> ; emp:name ?name }
* SQL Results SPARQL Results
* A_emp A_name ?emp ?name
@@ -143,6 +143,9 @@
}
}
+ /* bindOnPredicate: map a given triple to one or two joined tables, variable
+ * bindings to RelVarAttributes, and constraints if those variables were
+ * already bound. */
def bindOnPredicate(db:sql.DatabaseDesc, stateP:R2RState, triple:sparql.TriplePattern, enforceForeignKeys:Boolean):R2RState = {
val sparql.TriplePattern(s, p, o) = triple
p match {
@@ -333,7 +336,8 @@
Map[sparql.Var, SQL2RDFValueMapper](),
Set[sql.Expression]()
)
- val unionVars = disjoints.foldLeft(Set[sparql.Var]())((mySet,disjoint) => mySet ++ findVars(disjoint)).toList
+ val unionVars = disjoints.foldLeft(Set[sparql.Var]())((mySet,disjoint) =>
+ mySet ++ findVars(disjoint)).toList
/* Map the disjoints to subselects.
* <no> is used for uniquely naming flags in the SELECTs used to
@@ -343,7 +347,8 @@
val (subselects, no) = incPair
val disjointState = mapGraphPattern(db, emptyState, disjoint, enforceForeignKeys)
val disjointVars = findVars(disjoint)
- val disjointNo = sql.NamedAttribute(sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("" + no)), sql.AttrAlias(sql.Name("_DISJOINT_")))
+ val disjointNo = sql.NamedAttribute(sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("" + no)),
+ sql.AttrAlias(sql.Name("_DISJOINT_")))
val attrlist:Set[sql.NamedAttribute] = unionVars.foldLeft(Set(disjointNo))((attrs, v) => {
val attrOrNull = if (disjointState.varmap.contains(v)) varToAttribute(disjointState.varmap, v) else sql.ConstNULL()
@@ -366,14 +371,18 @@
* corefs (equivalence with earlier bindings).
* <no> is used for uniquely naming flags in the SELECTs used to
* indicate which disjoint produced a tuple.
+ * <state2> will have no additional tables in the TableList.
*/
val (state2, _) = disjoints.foldLeft((state, 0))((incPair,disjoint) => {
val (outerState, no) = incPair
val disjointState = mapGraphPattern(db, emptyState, disjoint, enforceForeignKeys)
val disjointVars = findVars(disjoint)
- val disjointNoAliasAttr = sql.RelAliasAttribute(unionAlias, sql.Attribute(sql.Name("_DISJOINT_")))
- val disjointCond = sql.RelationalExpressionNe(sql.PrimaryExpressionAttr(disjointNoAliasAttr), sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("" + no)))
+ /* Create a condition to test if this OPTIONAL was matched (called
+ * _DISJOINT_ as OPTIONAL behaves pretty much like a disjunction).
+ */
+ val disjointCond = sql.RelationalExpressionNe(sql.PrimaryExpressionAttr(sql.RelAliasAttribute(unionAlias, sql.Attribute(sql.Name("_DISJOINT_")))),
+ sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("" + no)))
val outerState2 = disjointVars.foldLeft(outerState)((myState, v) => {
val varAliasAttr = sql.RelAliasAttribute(unionAlias, sql.Attribute(sql.Name("A_" + v.s)))
if (myState.varmap.contains(v)) {
@@ -381,7 +390,6 @@
val newMap:Map[sparql.Var, SQL2RDFValueMapper] = if (varToAttribute(myState.varmap, v) == varAliasAttr) {
/* Same var was bound in an earlier disjoint. */
val oldDisjoints = varToAttributeDisjoints(myState.varmap, v)
- // myState
Map(v -> { disjointState.varmap(v) match {
case IntMapper(_, _) => IntMapper(varAliasAttr, oldDisjoints + disjointCond)
case StringMapper(_, _) => StringMapper(varAliasAttr, oldDisjoints + disjointCond)
@@ -394,10 +402,12 @@
val newConstraints =
if (varToAttribute(outerState.varmap, v) != varAliasAttr) {
/* Constraint against binding from earlier GP. */
- val constraint = sql.RelationalExpressionEq(sql.PrimaryExpressionAttr(varToAttribute(outerState.varmap, v)), sql.PrimaryExpressionAttr(varAliasAttr))
+ val constraint = sql.RelationalExpressionEq(sql.PrimaryExpressionAttr(varToAttribute(outerState.varmap, v)),
+ sql.PrimaryExpressionAttr(varAliasAttr))
if (varToAttributeDisjoints(outerState.varmap, v).size > 0)
- // (union0._DISJOINT_ != 0 AND union1._DISJOINT_ != 2) OR union0.x=union1.x
- varToAttributeDisjoints(outerState.varmap, v) map ((d) => sql.ExprDisjunction(Set(sql.ExprConjunction(Set(d, disjointCond)), constraint)))
+ // (union0._DISJOINT_ != 0 OR union0.x=union1.x) AND (union1._DISJOINT_ != 2 OR union0.x=union1.x)
+ varToAttributeDisjoints(outerState.varmap, v) map ((d) =>
+ sql.ExprDisjunction(Set(sql.ExprConjunction(Set(d, disjointCond)), constraint)))
else
Set(sql.ExprDisjunction(Set(disjointCond, constraint)))
} else {
@@ -452,7 +462,8 @@
*/
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 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))))
@@ -467,7 +478,8 @@
}
)
- /* Create a condition to test if this OPTIONAL was matched.
+ /* 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.RelAliasAttribute(leftJoinAlias, sql.Attribute(sql.Name("_DISJOINT_")))))
@@ -475,7 +487,9 @@
/* 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 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)) {
/* The variable has already been bound. */
@@ -494,7 +508,8 @@
Map()
val newConstraints = {
/* Constraint against binding from earlier GP. */
- val constraint = sql.RelationalExpressionEq(sql.PrimaryExpressionAttr(varToAttribute(state_postLeadingTable.varmap, v)), sql.PrimaryExpressionAttr(varAliasAttr))
+ val constraint = sql.RelationalExpressionEq(sql.PrimaryExpressionAttr(varToAttribute(state_postLeadingTable.varmap, v)),
+ sql.PrimaryExpressionAttr(varAliasAttr))
if (varToAttributeDisjoints(state_postLeadingTable.varmap, v).size > 0)
// (leftJoin0._DISJOINT_ IS NOT NULL AND leftJoin1._DISJOINT_ IS NOT NULL) OR leftJoin0.x=leftJoin1.x
varToAttributeDisjoints(state_postLeadingTable.varmap, v) map ((d) => sql.ExprDisjunction(Set(d, constraint)))