~ migrating RelationDesc to RDB.Relation
authorEric Prud'hommeaux <eric@w3.org>
Fri, 15 Oct 2010 12:21:49 -0400
changeset 234 84e78c96a219
parent 233 ebace24ecbc8
child 235 012a9d4711ac
~ migrating RelationDesc to RDB.Relation
src/main/scala/SQL.scala
src/main/scala/SparqlToSql.scala
src/test/scala/SQLTest.scala
--- a/src/main/scala/SQL.scala	Fri Oct 15 11:23:10 2010 -0400
+++ b/src/main/scala/SQL.scala	Fri Oct 15 12:21:49 2010 -0400
@@ -257,7 +257,7 @@
 
 sealed abstract class ValueDescription
 case class Value(datatype:Datatype) extends ValueDescription
-case class ForeignKey(rel:RDB.RelName, attr:RDB.AttrName) extends ValueDescription
+case class ForeignKey(rel:RDB.RelName, attr:RDB.CandidateKey) extends ValueDescription
 
 case class DatabaseDesc(relationdescs:Map[RDB.RelName,RelationDesc])
 object DatabaseDesc {
@@ -265,12 +265,12 @@
     DatabaseDesc(rs.map{r => (r.name -> r)}.toMap)
 }
 // case class Relation (name:RDB.RelName, header:RDB.Header, body:List[RDB.Tuple], candidates:List[RDB.CandidateKey], pk:Option[RDB.CandidateKey], fks:RDB.ForeignKeys)
-case class RelationDesc(name:RDB.RelName, attributes:Map[RDB.AttrName, ValueDescription], body:List[RDB.Tuple], candidates:List[RDB.CandidateKey], pk:Option[RDB.AttrName])
+case class RelationDesc(name:RDB.RelName, attributes:Map[RDB.AttrName, ValueDescription], body:List[RDB.Tuple], candidates:List[RDB.CandidateKey], pk:Option[RDB.CandidateKey])
 sealed abstract class FieldDescOrKeyDeclaration
 case class FieldDesc(attr:RDB.AttrName, value:Value, pkness:Boolean) extends FieldDescOrKeyDeclaration
 sealed abstract class KeyDeclaration extends FieldDescOrKeyDeclaration
-case class PrimaryKeyDeclaration(attr:RDB.AttrName) extends KeyDeclaration
-case class ForeignKeyDeclaration(fk:RDB.AttrName, rel:RDB.RelName, pk:RDB.AttrName) extends KeyDeclaration
+case class PrimaryKeyDeclaration(key:RDB.CandidateKey) extends KeyDeclaration
+case class ForeignKeyDeclaration(fk:List[RDB.AttrName], rel:RDB.RelName, pk:RDB.CandidateKey) extends KeyDeclaration
 case class View(rel:RDB.RelName, defn:SelectORUnion) { // sibling of RelationDesc
   override def toString = "CREATE VIEW " + rel + " AS\n" + defn
 }
@@ -294,10 +294,10 @@
     "CREATE" ~ "TABLE" ~ relation ~ "(" ~ rep1sep(fielddescorkeydef, ",") ~ ")" ^^
   {
     case "CREATE"~"TABLE"~relation~"("~reldesc~")" => {
-      val pk0:Option[RDB.AttrName] = None
+      val pk0:Option[RDB.CandidateKey] = None
       val attrs0 = Map[RDB.AttrName, ValueDescription]()
       val candidates0 = List[RDB.CandidateKey]()
-      val fks0 = Map[RDB.AttrName, ForeignKey]()
+      val fks0 = Map[List[RDB.AttrName], ForeignKey]()
       /* <pk>: (most recently parsed) PRIMARY KEY
        * <attrs>: map of attribute to type (e.g. INTEGER)
        * <fks>: map holding FOREIGN KEY relation REFERENCES attr
@@ -308,13 +308,13 @@
 	  rd match {
 	    case FieldDesc(attr, value, pkness) => {
 	      val (pkNew, candNew) =
-		if (pkness) (Some(attr), candidates ++ List(RDB.CandidateKey(attr.n)))
+		if (pkness) (Some(RDB.CandidateKey(List(attr))), candidates ++ List(RDB.CandidateKey(attr.n)))
 		else (pkopt, candidates)
 	      (pkNew, attrs + (attr -> value), candNew, fks)
 	    }
-	    case PrimaryKeyDeclaration(attr) =>
+	    case PrimaryKeyDeclaration(key) =>
 	      // @@ why doesn't [[ candidates + RDB.CandidateKey(attr.n) ]] work?
-	      (Some(attr), attrs, candidates ++ List(RDB.CandidateKey(attr.n)), fks)
+	      (Some(key), attrs, candidates ++ List(RDB.CandidateKey(key map {attr => RDB.AttrName(attr.n)})), fks)
 	    case ForeignKeyDeclaration(fk, rel, pk) =>
 	      (pkopt, attrs, candidates, fks + (fk -> ForeignKey(rel, pk)))
 	  }
@@ -324,7 +324,8 @@
        */
       val attrs2 = attrs.map(x => {
 	val (attr:RDB.AttrName, value:Value) = x
-	if (fks.contains(attr))	(attr -> fks(attr))
+	val asKey = RDB.CandidateKey(List(attr))
+	if (fks.contains(asKey)) (attr -> fks(asKey))
 	else (attr -> value)
       })
       val rd = RelationDesc(relation, attrs2, List(), candidates, pk)
@@ -335,10 +336,10 @@
   def fielddescorkeydef:Parser[FieldDescOrKeyDeclaration] = (
       attribute ~ typpe ~ opt("PRIMARY" ~ "KEY") ^^
       { case attribute~typpe~pkness => FieldDesc(attribute, typpe, pkness.isDefined) }
-    | "PRIMARY" ~ "KEY" ~ "(" ~ attribute ~ ")" ^^
-      { case "PRIMARY"~"KEY"~"("~attribute~")" => PrimaryKeyDeclaration(attribute) }
-    | "FOREIGN" ~ "KEY" ~ "(" ~ attribute ~ ")" ~ "REFERENCES" ~ relation ~ "(" ~ attribute ~ ")" ^^
-      { case "FOREIGN"~"KEY"~"("~fk~")"~"REFERENCES"~relation~"("~pk~")" => ForeignKeyDeclaration(fk, relation, pk) }
+    | "PRIMARY" ~ "KEY" ~ "(" ~ rep1sep(attribute, ",") ~ ")" ^^
+      { case "PRIMARY"~"KEY"~"("~attributes~")" => PrimaryKeyDeclaration(RDB.CandidateKey(attributes)) }
+    | "FOREIGN" ~ "KEY" ~ "(" ~ rep1sep(attribute, ",") ~ ")" ~ "REFERENCES" ~ relation ~ "(" ~ rep1sep(attribute, ",") ~ ")" ^^
+      { case "FOREIGN"~"KEY"~"("~fk~")"~"REFERENCES"~relation~"("~pk~")" => ForeignKeyDeclaration(fk, relation, RDB.CandidateKey(pk)) }
   )
 
   def typpe:Parser[Value] = (
--- a/src/main/scala/SparqlToSql.scala	Fri Oct 15 11:23:10 2010 -0400
+++ b/src/main/scala/SparqlToSql.scala	Fri Oct 15 12:21:49 2010 -0400
@@ -289,7 +289,7 @@
      */
     val binding = reldesc.pk match {
       /** <pre>varConstraint(R_emp, Some(empid), VarAssignable(?emp), Employee) -&gt; RDFNoder(Employee,FullBinding(R_emp.empid))</pre> */
-      case Some(sql.RDB.AttrName(constrainMe.attribute.n)) => RDFNoder(rel, boundTo)
+      case Some(sql.RDB.CandidateKey(List(sql.RDB.AttrName(constrainMe.attribute.n)))) => RDFNoder(rel, boundTo)
       case _ => {
 
 	if (reldesc.attributes.contains(constrainMe.attribute)) {
@@ -357,6 +357,12 @@
   //   }
   // }
 
+  def __optFirst (k:Option[sql.RDB.CandidateKey]):Option[sql.RDB.AttrName] =
+    k match {
+      case Some(ck) => Some(ck.attrs(0))
+      case _ => None
+    }
+
   /**
    * map a given triple to one or two joined tables, variable
    * @param db  database description
@@ -390,11 +396,11 @@
 	val state_postSubj = s match {
 	  case sparql.TermUri(u) =>
 	    /** additional constraint, e.g. R_empid18.empid=18. */
-	    uriConstraint(stateP, sql.RelVarAttr(relvar, db.relationdescs(rel).pk.get), parseObjectURI(u))
+	    uriConstraint(stateP, sql.RelVarAttr(relvar, db.relationdescs(rel).pk.get.attrs(0)), parseObjectURI(u)) // !! (0)
 	  case sparql.TermVar(v) =>
 	    /** assignable binding for novel vars, new constraint for previously bound vars. */
 	    try {
-	      varConstraint(stateP, relvar, db.relationdescs(rel).pk, sparql.VarAssignable(v), db, rel)
+	      varConstraint(stateP, relvar, __optFirst(db.relationdescs(rel).pk), sparql.VarAssignable(v), db, rel) // !! (0)
 	    } catch {
 	      case e:java.util.NoSuchElementException =>
 		/** Tell user that the relation.attribute encoded in the subject was not found in the database description. */
@@ -403,7 +409,7 @@
 	  case sparql.TermBNode(b) =>
 	    /** assignable binding for novel bnodes, new constraint for previously bound bnodes. */
 	    try {
-	      varConstraint(stateP, relvar, db.relationdescs(rel).pk, sparql.BNodeAssignable(b), db, rel)
+	      varConstraint(stateP, relvar, __optFirst(db.relationdescs(rel).pk), sparql.BNodeAssignable(b), db, rel) // !! (0)
 	    } catch {
 	      case e:java.util.NoSuchElementException =>
 		throw new Exception("error processing { " + s + " " + p + " " + o + " } :db.relationdescs(" + rel + ") not found in " + db)
@@ -427,17 +433,17 @@
 	 */
 	val (targetattr:sql.RelVarAttr, targetrel, dt, state_fkeys:R2RState) = db.relationdescs(rel).attributes(attr) match {
 	  case sql.ForeignKey(fkrel, fkattr) => {
-	    try { db.relationdescs(fkrel).attributes(fkattr) } catch {
+	    try { db.relationdescs(fkrel).attributes(fkattr.attrs(0)) } catch { // !! (0)
 	      /** Foreign key relation.attribute was not found in the database description. */
 	      case e:java.util.NoSuchElementException =>
 		throw new Exception("db.relationdescs(" + fkrel + ").attributes(" + fkattr + ") not found in " + db)
 	    }
-	    val fkdt = db.relationdescs(fkrel).attributes(fkattr) match {
+	    val fkdt = db.relationdescs(fkrel).attributes(fkattr.attrs(0)) match { // !! (0)
 	      /** Foreign key to something which is a foreign key. May have use
 	       * cases, but signal error until we figure out that they are. */
 	      case sql.ForeignKey(dfkrel, dfkattr) => error("foreign key " + rel.n + "." + attr.n + 
-							"->" + fkrel.n + "." + fkattr.n + 
-							"->" + dfkrel.n + "." + dfkattr.n)
+							"->" + fkrel.n + "." + fkattr.attrs(0).n +  // !! (0)
+							"->" + dfkrel.n + "." + dfkattr.attrs(0).n) // !! (0)
 	      case sql.Value(x) => x
 	    }
 	    if (enforceForeignKeys) {
@@ -449,7 +455,7 @@
 	       * and bind targetattr:R_who.empid. targetrel:Employee .
 	       */
 	      val oRelVar = relVarFromTerm(o)
-	      val fkaliasattr = sql.RelVarAttr(oRelVar, fkattr)
+	      val fkaliasattr = sql.RelVarAttr(oRelVar, fkattr.attrs(0)) // !! (0)
 	      val state_t = R2RState(state_subjJoin.joins + sql.InnerJoin(sql.AliasedResource(fkrel,oRelVar), None),
 				     state_subjJoin.varmap,
 				     state_subjJoin.exprs + sql.RelationalExpressionEq(sql.PrimaryExpressionAttr(fkaliasattr),
--- a/src/test/scala/SQLTest.scala	Fri Oct 15 11:23:10 2010 -0400
+++ b/src/test/scala/SQLTest.scala	Fri Oct 15 12:21:49 2010 -0400
@@ -436,7 +436,7 @@
 		   Map(RDB.AttrName("ID") -> Value(Datatype.INTEGER)),
 		   List(),
 		   List(RDB.CandidateKey("ID")),
-		   Option(RDB.AttrName("ID"))))
+		   Option(RDB.CandidateKey("ID"))))
       assert(expected === (a.parseAll(a.ddl, e).get))
   }
 
@@ -452,7 +452,7 @@
 		       RDB.AttrName("EnterpriseEntryID") -> Value(Datatype.INTEGER)),
 		   List(),
 		   List(RDB.CandidateKey("ID")),
-		   Option(RDB.AttrName("ID"))))
+		   Option(RDB.CandidateKey("ID"))))
 
     assert(expected === (a.parseAll(a.ddl, e).get))
   }
@@ -468,7 +468,7 @@
 		       RDB.AttrName("EnterpriseEntryID") -> Value(Datatype.INTEGER)),
 		   List(),
 		   List(RDB.CandidateKey("ID")),
-		   Option(RDB.AttrName("ID"))))
+		   Option(RDB.CandidateKey("ID"))))
 
     assert(expected === (a.parseAll(a.ddl, e).get))
   }
@@ -484,16 +484,16 @@
 		   Map(RDB.AttrName("ID") -> Value(Datatype.INTEGER),
 		       RDB.AttrName("MiddleName") -> Value(Datatype.STRING),
 		       RDB.AttrName("DateOfBirth") -> Value(Datatype.DATE),
-		       RDB.AttrName("SexDE") -> ForeignKey(RDB.RelName("Sex_DE"), RDB.AttrName("ID"))),
+		       RDB.AttrName("SexDE") -> ForeignKey(RDB.RelName("Sex_DE"), RDB.CandidateKey("ID"))),
 		   List(),
 		   List(RDB.CandidateKey("ID")),
-		   Option(RDB.AttrName("ID"))),
+		   Option(RDB.CandidateKey("ID"))),
       RelationDesc("Sex_DE",
 		   Map(RDB.AttrName("ID") -> Value(Datatype.INTEGER),
 		       RDB.AttrName("EntryName") -> Value(Datatype.STRING)),
 		   List(),
 		   List(RDB.CandidateKey("ID")),
-		   Option(RDB.AttrName("ID")))
+		   Option(RDB.CandidateKey("ID")))
     )
     assert(expected === (a.parseAll(a.ddl, e).get))
   }
@@ -513,44 +513,44 @@
 		 Map(RDB.AttrName("ID") -> Value(Datatype.INTEGER),
 		     RDB.AttrName("MiddleName") -> Value(Datatype.STRING),
 		     RDB.AttrName("DateOfBirth") -> Value(Datatype.DATE),
-		     RDB.AttrName("SexDE") -> ForeignKey(RDB.RelName("Sex_DE"), RDB.AttrName("ID"))),
+		     RDB.AttrName("SexDE") -> ForeignKey(RDB.RelName("Sex_DE"), RDB.CandidateKey("ID"))),
 		 List(),
 		 List(RDB.CandidateKey("ID")),
-		 Option(RDB.AttrName("ID"))),
+		 Option(RDB.CandidateKey("ID"))),
     RelationDesc("Sex_DE",
 		 Map(RDB.AttrName("ID") -> Value(Datatype.INTEGER),
 		     RDB.AttrName("EntryName") -> Value(Datatype.STRING)),
 		 List(),
 		 List(RDB.CandidateKey("ID")),
-		 Option(RDB.AttrName("ID"))),
+		 Option(RDB.CandidateKey("ID"))),
     RelationDesc("Item_Medication",
 		 Map(RDB.AttrName("ID") -> Value(Datatype.INTEGER),
-		     RDB.AttrName("PatientID") -> ForeignKey(RDB.RelName("Person"),  RDB.AttrName("ID")),
+		     RDB.AttrName("PatientID") -> ForeignKey(RDB.RelName("Person"), RDB.CandidateKey("ID")),
 		     RDB.AttrName("PerformedDTTM") -> Value(Datatype.DATE),
 		     RDB.AttrName("EntryName") -> Value(Datatype.STRING)),
 		 List(),
 		 List(RDB.CandidateKey("ID")),
-		 Option(RDB.AttrName("ID"))),
+		 Option(RDB.CandidateKey("ID"))),
     RelationDesc("Medication",
 		 Map(RDB.AttrName("ID") -> Value(Datatype.INTEGER),
-		     RDB.AttrName("ItemID") -> ForeignKey(RDB.RelName("Item_Medication"),  RDB.AttrName("ID")),
-		     RDB.AttrName("MedDictDE") -> ForeignKey(RDB.RelName("Medication_DE"), RDB.AttrName("ID"))),
+		     RDB.AttrName("ItemID") -> ForeignKey(RDB.RelName("Item_Medication"), RDB.CandidateKey("ID")),
+		     RDB.AttrName("MedDictDE") -> ForeignKey(RDB.RelName("Medication_DE"), RDB.CandidateKey("ID"))),
 		 List(),
 		 List(RDB.CandidateKey("ID")),
-		 Option(RDB.AttrName("ID"))),
+		 Option(RDB.CandidateKey("ID"))),
     RelationDesc("Medication_DE",
 		 Map(RDB.AttrName("ID") -> Value(Datatype.INTEGER),
 		     RDB.AttrName("NDC") -> Value(Datatype.INTEGER)),
 		 List(),
 		 List(RDB.CandidateKey("ID")),
-		 Option(RDB.AttrName("ID"))),
+		 Option(RDB.CandidateKey("ID"))),
     RelationDesc("NDCcodes",
 		 Map(RDB.AttrName("ID") -> Value(Datatype.INTEGER),
 		     RDB.AttrName("NDC") -> Value(Datatype.INTEGER),
 		     RDB.AttrName("ingredient") -> Value(Datatype.INTEGER)),
 		 List(),
 		 List(RDB.CandidateKey("ID")),
-		 Option(RDB.AttrName("ID")))
+		 Option(RDB.CandidateKey("ID")))
   )
     assert(expected === (a.parseAll(a.ddl, e).get))
   }