--- a/directmapping-test/src/main/scala/DirectMappingTestSuite.scala Sat Feb 12 11:41:00 2011 -0500
+++ b/directmapping-test/src/main/scala/DirectMappingTestSuite.scala Sat Feb 12 13:19:54 2011 -0500
@@ -39,11 +39,6 @@
}
-
-
-
-
-
trait DirectMappingTest extends FunSuite with RDFModel with RDFImplicits with DirectMappingModule with TurtleModule {
import DirectMapping._
--- a/directmapping/src/main/scala/DirectMapping.scala Sat Feb 12 11:41:00 2011 -0500
+++ b/directmapping/src/main/scala/DirectMapping.scala Sat Feb 12 13:19:54 2011 -0500
@@ -52,19 +52,15 @@
* <http://www.w3.org/2001/sw/rdb2rdf/directGraph/>
*/
- def references (t:Tuple, r:Relation):Set[ForeignKeyKey] = {
- val allFKs:Set[ForeignKeyKey] = r.fks.keySet
- val nulllist:Set[AttrName] = t.nullAttributes(r.header)
- val nullFKs:Set[ForeignKeyKey] = allFKs filter { fk => (nulllist & fk.toSet) nonEmpty }
- r.fks.keySet -- nullFKs
+ def references (t:Tuple, r:Relation):Set[ForeignKey] = {
+ val nulls:Set[AttrName] = t.nullAttributes
+ val references = r.fks filter { case ForeignKey(as, _) => nulls & as.toSet isEmpty }
+ references
}
-
+
def scalars (t:Tuple, r:Relation):Set[AttrName] = {
- val allAttrs:Set[AttrName] = r.header.keySet
- val allFKs:Set[ForeignKeyKey] = r.fks.keySet
- val unaryFKs:Set[AttrName] = allFKs map { _.attrs } filter { _.length == 1 } flatten
-
- allAttrs -- unaryFKs
+ val notNulls:Set[AttrName] = t.notNullAttributes
+ notNulls filterNot { attrName => r.fks definesActuallyUnaryFK attrName }
}
/** The NodeMap-generating functions: */
@@ -81,7 +77,7 @@
r.pk match {
case Some(pk) =>
/** Table has a primkary key. */
- NodeIRI(nodemap(r.name, pk.attrs, t.lexvaluesNoNulls(pk.attrs)))
+ NodeIRI(nodemap(r.name, pk.attrs, t.notNullLexvalues(pk)))
case None =>
/** Table has no primkary key (but has some candidate keys). */
NodeBNode(freshbnode())
@@ -108,7 +104,7 @@
r.candidates.headOption match {
// Known to have at least one key, so take the first one.
case Some(firstKey) => {
- val vs = t.lexvaluesNoNulls(firstKey.attrs)
+ val vs = t.notNullLexvalues(firstKey)
nodes.ultimateReferent(r.name, firstKey, vs, db)
}
/** Table has no candidate keys. */
@@ -135,8 +131,8 @@
}
def directL (rn:RelName, s:Node, a:AttrName, h:Header, t:Tuple) : Option[Triple] = {
- val p = predicatemap (rn, List(a))
- t.lexvalue(a) match {
+ val p = predicatemap (rn, new AttrList { val attrs = List(a) } )
+ t(a) match {
case l:LexicalValue => {
val o = literalmap(l, h.sqlDatatype(a))
Some(Triple(SubjectNode(s),
@@ -147,19 +143,19 @@
}
}
- def directN (s:Node, as:ForeignKeyKey, r:Relation, t:Tuple, nodes:NodeMap) : Triple = {
- val p = predicatemap (r.name, as.attrs)
- val ls:List[LexicalValue] = t.lexvaluesNoNulls(as.attrs)
- val target = r.fks(as)
- if (!nodes.contains(target.rel))
- error("No referent relation \"" + target.rel + "\" to match " + r.name + t)
- if (!nodes(target.rel).contains(target.key))
- error("Relation " + target.rel + " has no attributes (" + target.key + ") to match " + r.name + t)
- if (!nodes(target.rel)(target.key).contains(ls))
- error("Relation " + target.rel + "(" + target.key + ") has no values " + ls + " to match " + r.name + t)
- val o:Object = ObjectNode(nodes(target.rel)(target.key)(ls))
- Triple(SubjectNode(s), PredicateIRI(p), o)
- }
+ def directN (s:Node, fk:ForeignKey, r:Relation, t:Tuple, nodes:NodeMap) : Triple = {
+ val p = predicatemap (r.name, fk)
+ val ls:List[LexicalValue] = t.notNullLexvalues(fk)
+ val ForeignKey(as, Target(rel, key)) = fk
+ if (!nodes.contains(rel))
+ error("No referent relation \"" + rel + "\" to match " + r.name + t)
+ if (!nodes(rel).contains(key))
+ error("Relation " + rel + " has no attributes (" + key + ") to match " + r.name + t)
+ if (!nodes(rel)(key).contains(ls))
+ error("Relation " + rel + "(" + key + ") has no values " + ls + " to match " + r.name + t)
+ val o:Object = ObjectNode(nodes(rel)(key)(ls))
+ Triple(SubjectNode(s), PredicateIRI(p), o)
+ }
// These implicits make nodemap and predicatemap functions prettier.
implicit def relName2string (rn:RelName) = rn.n
@@ -170,8 +166,8 @@
IRI(UE(rn) + "/" + pairs.mkString("_") + "#_")
}
- def predicatemap (rn:RelName, as:List[AttrName]) : IRI =
- IRI(UE(rn) + "#" + as.mkString("_"))
+ def predicatemap (rn:RelName, as:AttrList) : IRI =
+ IRI(UE(rn) + "#" + as.attrs.mkString("_"))
// TODO: aren't they already part of the RDF model?
def XSD (d:Datatype) : IRI =
--- a/rdb/src/main/scala/RDB.scala Sat Feb 12 11:41:00 2011 -0500
+++ b/rdb/src/main/scala/RDB.scala Sat Feb 12 13:19:54 2011 -0500
@@ -30,27 +30,34 @@
Header(s map { case (name, datatype) => (AttrName(name), datatype) } toMap)
}
- type AttrList = List[AttrName]
- case class ForeignKeyKey (attrs:AttrList) {
+ trait AttrList {
+ val attrs:List[AttrName]
+ def isUnary:Boolean = attrs.length == 1
+ }
+
+ case class ForeignKey (attrs:List[AttrName], target:Target) extends AttrList {
def toSet = attrs.toSet
}
- case class CandidateKey (attrs:AttrList)
+ case class CandidateKey (attrs:List[AttrName]) extends AttrList
object CandidateKey {
def apply (l:String*):CandidateKey =
CandidateKey(l.toList map { AttrName(_) })
}
implicit def cc2list (cc:CandidateKey) = cc.attrs
- case class ForeignKeys (m:Map[ForeignKeyKey, Target]) {
- def apply (l:ForeignKeyKey) = m(l)
- def keySet = m.keySet.toSet
- def contains (l:AttrList) = m contains ForeignKeyKey(l) // self-promoting cheat
- def refdAttrs (kk:ForeignKeyKey) = m(kk).key.attrs
+ case class ForeignKeys (fks:Set[ForeignKey]) {
+ def has(as:AttrList) = fks exists { _.attrs == as }
+ def getFK(as:AttrList):ForeignKey = fks find { _.attrs == as } get
+ def unaryFKs:Set[ForeignKey] = fks filter { _.isUnary }
+ def definesActuallyUnaryFK(a:AttrName):Boolean = unaryFKs exists { _.attrs contains a }
+ def filter(p: ForeignKey => Boolean):Set[ForeignKey] = fks filter p
+ def -(as:AttrList):ForeignKeys = ForeignKeys(fks filter { _.attrs == as })
}
+
object ForeignKeys {
- def apply (s:(List[String], Target)*):ForeignKeys =
- ForeignKeys(s map { case (keys, target) => (ForeignKeyKey(keys map { AttrName(_) }), target)} toMap)
+ def apply (fks:(List[String], Target)*):ForeignKeys =
+ ForeignKeys(fks map { case (keys, target) => ForeignKey(keys map { AttrName(_) }, target)} toSet)
}
case class Target (rel:RelName, key:CandidateKey)
@@ -71,11 +78,25 @@
val DATETIME = Datatype("Datetime")
}
+ // case class Tuple (m:Map[AttrName, CellValue]) {
+ // def apply (a:AttrName) = m(a)
+ // def lexvalue (a:AttrName) : CellValue = m(a)
+ // def lexvaluesNoNulls (as:List[AttrName]) = as map { m(_).asInstanceOf[LexicalValue] }
+ // def nullAttributes (h:Header) : Set[AttrName] = h.keySet filter { m(_) == ␀ }
+ // }
case class Tuple (m:Map[AttrName, CellValue]) {
def apply (a:AttrName) = m(a)
- def lexvalue (a:AttrName) : CellValue = m(a)
- def lexvaluesNoNulls (as:List[AttrName]) = as map { m(_).asInstanceOf[LexicalValue] }
- def nullAttributes (h:Header) : Set[AttrName] = h.keySet filter { m(_) == ␀ }
+ // assumes that the AttrName does not correspond to null values
+ // for example, it's ok to call it with PK attributes
+ def notNullLexvalues (as:AttrList):List[LexicalValue] =
+ as.attrs map {
+ m(_) match {
+ case lexicalValue @ LexicalValue(_) => lexicalValue
+ case ␀ => error("this value MUST not be null")
+ }
+ }
+ def nullAttributes : Set[AttrName] = m collect { case (attrName, cellValue) if cellValue == ␀ => attrName } toSet
+ def notNullAttributes : Set[AttrName] = m collect { case (attrName, cellValue) if cellValue != ␀ => attrName } toSet
}
object Tuple {
def apply (s:(String, CellValue)*):Tuple =
--- a/sql/src/main/scala/SQL.scala Sat Feb 12 11:41:00 2011 -0500
+++ b/sql/src/main/scala/SQL.scala Sat Feb 12 13:19:54 2011 -0500
@@ -168,7 +168,7 @@
sealed abstract class KeyDeclaration extends FieldDescOrKeyDeclaration
case class PrimaryKeyDeclaration(key:RDB.CandidateKey) extends KeyDeclaration
case class CandidateKeyDeclaration(key:RDB.CandidateKey) extends KeyDeclaration
-case class ForeignKeyDeclaration(fk:RDB.ForeignKeyKey, rel:RDB.RelName, pk: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 RDB.Relation
override def toString = "CREATE VIEW " + rel + " AS\n" + defn
}
@@ -213,14 +213,13 @@
val pk0:Option[RDB.CandidateKey] = None
val attrs0 = Map[RDB.AttrName, RDB.Datatype]()
val candidates0 = List[RDB.CandidateKey]()
- val fks0 = Map[RDB.ForeignKeyKey, RDB.Target]()
+ val fks0 = Set[RDB.ForeignKey]()
/* <pk>: (most recently parsed) PRIMARY KEY
* <attrs>: map of attribute to type (e.g. INTEGER)
* <fks>: map holding FOREIGN KEY relation REFERENCES attr
*/
val (pk, attrs, candidates, fks) =
- reldesc.foldLeft((pk0, attrs0, candidates0, fks0))((p, rd) => {
- val (pkopt, attrs, candidates, fks) = p
+ reldesc.foldLeft((pk0, attrs0, candidates0, fks0)) { case ((pkopt, attrs, candidates, fks), rd) => {
rd match {
case FieldDesc(attr, value, pkness) => {
val (pkNew, candNew) =
@@ -230,14 +229,14 @@
}
case PrimaryKeyDeclaration(key) =>
// @@ why doesn't [[ candidates + RDB.CandidateKey(attr.n) ]] work?
- (Some(key), attrs, candidates ++ List(RDB.CandidateKey(key map {attr => RDB.AttrName(attr.n)})), fks)
+ (Some(key), attrs, candidates :+ key, fks)
case CandidateKeyDeclaration(key) =>
// @@ why doesn't [[ candidates + RDB.CandidateKey(attr.n) ]] work?
- (pkopt, attrs, candidates ++ List(RDB.CandidateKey(key map {attr => RDB.AttrName(attr.n)})), fks)
+ (pkopt, attrs, candidates :+ key, fks)
case ForeignKeyDeclaration(fk, rel, pk) =>
- (pkopt, attrs, candidates, fks + (fk -> RDB.Target(rel, pk)))
+ (pkopt, attrs, candidates, fks + RDB.ForeignKey(fk, RDB.Target(rel, pk)))
}
- })
+ } }
val rd = RDB.Relation(relation, RDB.Header(attrs), List(), candidates, pk, RDB.ForeignKeys(fks))
Create(relation, rd)
}
@@ -262,8 +261,7 @@
| "(?i)UNIQUE".r ~ "(" ~ rep1sep(attribute, ",") ~ ")" ^^
{ case _~"("~attributes~")" => CandidateKeyDeclaration(RDB.CandidateKey(attributes)) }
| "(?i)FOREIGN".r ~ "(?i)KEY".r ~ "(" ~ rep1sep(attribute, ",") ~ ")" ~ "(?i)REFERENCES".r ~ relation ~ "(" ~ rep1sep(attribute, ",") ~ ")" ^^
- { case _~_~"("~fk~")"~_~relation~"("~pk~")" => ForeignKeyDeclaration(RDB.ForeignKeyKey(fk), relation, RDB.CandidateKey(pk)) }
- )
+ { case _~_~"("~fk~")"~_~relation~"("~pk~")" => ForeignKeyDeclaration(fk, relation, RDB.CandidateKey(pk)) } )
def typpe:Parser[RDB.Datatype] = (
"(?i)INTEGER".r ~ opt(size)^^ { case _ => RDB.Datatype.INTEGER }
--- a/sql/src/test/scala/SQLTest.scala Sat Feb 12 11:41:00 2011 -0500
+++ b/sql/src/test/scala/SQLTest.scala Sat Feb 12 13:19:54 2011 -0500
@@ -792,7 +792,6 @@
FOREIGN KEY (worker) REFERENCES People(ID),
project STRING, PRIMARY KEY (worker, project),
deptName STRING, deptCity STRING,
- FOREIGN KEY (worker) REFERENCES People(ID),
FOREIGN KEY (project, deptName, deptCity) REFERENCES Projects(name, deptName, deptCity),
FOREIGN KEY (deptName, deptCity) REFERENCES Department(name, city));
INSERT INTO TaskAssignments (worker, project, deptName, deptCity) VALUES (7, "pencil survey", "accounting", "Cambridge");