--- a/src/main/scala/Main.scala Mon Sep 27 09:31:57 2010 -0400
+++ b/src/main/scala/Main.scala Mon Sep 27 17:46:02 2010 -0400
@@ -6,7 +6,7 @@
object SQL {
case class Database(m:Map[RelName, Relation])
- case class Relation (header:Header, body:Body, keys:List[CandidateKey], pk:CandidateKey, fks:ForeignKeys)
+ case class Relation (header:Header, body:Body, keys:List[CandidateKey], pk:Option[CandidateKey], fks:ForeignKeys)
case class Header (types:Map[AttrName, SQLDatatype]) {
def keySet () = types.keySet
}
@@ -17,12 +17,12 @@
type Body = Set[Tuple]
+ type Tuple = Map[AttrName, CellValue]
+
abstract class CellValue
case class LexicalValue (s:String) extends CellValue
case class ␀ () extends CellValue
- type Tuple = Map[AttrName, CellValue]
-
sealed abstract class SQLDatatype
case class SQLInt () extends SQLDatatype
case class SQLFloat () extends SQLDatatype
@@ -37,7 +37,7 @@
type AttrName = String
// Accessor functions:
- def pk (r:Relation) : List[AttrName] = r.pk
+ def pk (r:Relation) : Option[List[AttrName]] = r.pk
def header (r:Relation) : Header = r.header
def body (r:Relation) : Body = r.body
@@ -106,29 +106,12 @@
def databasemap (u:StemIRI, db:Database) : RDFGraph = {
val idxables:Set[RelName] = db.m.keySet.filter(rn => db.m(rn).keys.size > 0)
val nodes:NodeMap = idxables.map(rn => rn -> relation2subject(u, rn, db.m(rn))).toMap
- db.m.keySet.flatMap(rn => relationmap(u, rn, db.m(rn), nodes)).toSet
+ db.m.keySet.flatMap(rn => relationmap(u, rn, db.m(rn), nodes))
}
def relation2subject (u:StemIRI, rn:RelName, r:Relation) : KeyMap = {
- val l = List((List("KeyA", "KeyB"), List(10, 11), 1), (List("KeyA", "KeyB"), List(20, 21), 2))
- val g = Map("KeyA" -> Map("ValA1" -> 1, "ValA2" -> 2), "KeyB" -> Map("ValB1" -> 1, "ValB2" -> 2))
- l.foldLeft(Map[String, Map[Int, Int]]())((m, t) => {
- val pairs = t._1.zip(t._2)
- pairs.foldLeft(m)((m, p) => {
- if (m.get(p._1).isDefined) {
- val byKey = m(p._1)
- if (byKey.get(p._2).isDefined) {
- error("tried to set " + rn + p._1 + p._2 + " = " + t._3 + "(was " + byKey(p._2) + ")")
- } else {
- val im1 = byKey ++ Map[Int, Int](p._2 -> t._3)
- m ++ Map[String, Map[Int, Int]](p._1 -> im1)
- }
- } else {
- m ++ Map(p._1 -> Map(p._2 -> t._3))
- }
- })
- })
+ // Here's a built-In test to make sure we don't screw this up:
val ck1:CandidateKey = List("name", "ssn")
val ck2:CandidateKey = List("ID")
val v11:List[CellValue] = List(LexicalValue("bob"), LexicalValue("123"))
@@ -164,6 +147,7 @@
v22 -> s2))
assert(goal == test)
+ // Now the useful invocation:
val data2:Set[(List[(CandidateKey, List[CellValue])], Node)] = body(r).map(t => {
// (List(List("name", "ssn"), List("ID")), List(List("bob", 123), List(18)), 1)
// (List(List("name", "ssn"), List("ID")), List(List("alice", 8), List(23)), 2)
@@ -190,8 +174,12 @@
def tuple2subject (u:StemIRI, rn:RelName, t:Tuple, r:Relation) : (List[(CandidateKey, List[CellValue])], Node) = {
val h = header(r)
- val vs = pk(r).map(k => lexvalue(h, t, k).asInstanceOf[LexicalValue])
- val s:Node = if (pk(r).length == 0) freshbnode() else nodemap(u, rn, pk(r), vs) // Assume: no NULLs in primary key
+ val s:Node =
+ if (r.pk.isDefined) {
+ val vs = r.pk.get.map(k => lexvalue(h, t, k).asInstanceOf[LexicalValue])
+ nodemap(u, rn, r.pk.get, vs) // Assume: no NULLs in primary key
+ } else
+ freshbnode()
(r.keys.map(k => {
val values:List[CellValue] = k.map(a => t(a))
(k, values)
@@ -203,8 +191,14 @@
def tuplemap (u:StemIRI, rn:RelName, t:Tuple, r:Relation, nodes:NodeMap) : Set[Triple] = {
val h = header(r)
- val vs = pk(r).map(k => lexvalue(h, t, k).asInstanceOf[LexicalValue])
- val s:Node = if (nodes.get(rn).isDefined) nodes(rn)(pk(r))(vs) else freshbnode()
+ val s:Node =
+ if (nodes.get(rn).isDefined) {
+ // Known to have at least one key, so take the first one.
+ val k = r.keys(0)
+ val vs = k.map(a => lexvalue(h, t, a).asInstanceOf[LexicalValue])
+ nodes(rn)(k)(vs)
+ } else
+ freshbnode()
val allAttrs:Set[AttrName] = h.keySet
val allFKs:Set[List[AttrName]] = r.fks.keySet
@@ -224,7 +218,12 @@
}
- def freshbnode () : Node = BNode("999")
+ var NextBNode = 97
+ def freshbnode () : Node = {
+ val ret = NextBNode
+ NextBNode = NextBNode + 1
+ BNode(ret.toChar.toString)
+ }
def scalartriples (u:StemIRI, rn:RelName, s:Node, a:AttrName, h:Header, t:Tuple) : Triple = {
val p = predicatemap (u, rn, List(a))
@@ -272,6 +271,6 @@
def literalmap (l:LexicalValue, d:SQLDatatype) : TypedLiteral =
TypedLiteral(l.s, XSD(d))
- def UE (s:String) : String = s
+ def UE (s:String) : String = s.replaceAll(" ", "+")
}
--- a/src/test/scala/Test.scala Mon Sep 27 09:31:57 2010 -0400
+++ b/src/test/scala/Test.scala Mon Sep 27 17:46:02 2010 -0400
@@ -17,7 +17,7 @@
"city" -> LexicalValue("Cambridge"),
"state" -> LexicalValue("MA"))),
List(List("ID")),
- List("ID"),
+ Some(List("ID")),
Map())
val people = Relation(Header(Map("ID" -> SQLInt(),
@@ -30,7 +30,7 @@
"fname" -> LexicalValue("Sue"),
"addr" -> ␀())),
List(List("ID")),
- List("ID"),
+ Some(List("ID")),
Map(List("addr") -> Target("Addresses", List("ID"))))
val db = Database(Map("Addresses" -> addresses,
@@ -52,6 +52,7 @@
assert (expected === g)
}
+
test("2 People 1 Addresses 1 Department") {
val addresses = Relation(Header(Map("ID" -> SQLInt(),
@@ -61,7 +62,7 @@
"city" -> LexicalValue("Cambridge"),
"state" -> LexicalValue("MA"))),
List(List("ID")),
- List("ID"),
+ Some(List("ID")),
Map())
val people = Relation(Header(Map("ID" -> SQLInt(),
@@ -80,7 +81,7 @@
"deptName" -> ␀(),
"deptCity" -> ␀())),
List(List("ID")),
- List("ID"),
+ Some(List("ID")),
Map(List("addr") -> Target("Addresses", List("ID")),
List("deptName", "deptCity") -> Target("Department", List("name", "city"))))
@@ -93,7 +94,7 @@
"city" -> LexicalValue("Cambridge"),
"manager" -> LexicalValue("8"))),
List(List("ID"), List("name", "city")),
- List("ID"),
+ Some(List("ID")),
Map(List("manager") -> Target("People", List("ID"))))
val db = Database(Map("Addresses" -> addresses,
@@ -125,6 +126,135 @@
assert (expected === g)
}
+
+ test("2 People 1 Addresses 1 Department 2 Projects 1 Task") {
+
+ val addresses = Relation(Header(Map("ID" -> SQLInt(),
+ "city" -> SQLString(),
+ "state" -> SQLString())),
+ Set(Map("ID" -> LexicalValue("18"),
+ "city" -> LexicalValue("Cambridge"),
+ "state" -> LexicalValue("MA"))),
+ List(List("ID")),
+ Some(List("ID")),
+ Map())
+
+ val people = Relation(Header(Map("ID" -> SQLInt(),
+ "fname" -> SQLString(),
+ "addr" -> SQLInt(),
+ "deptName" -> SQLString(),
+ "deptCity" -> SQLString())),
+ Set(Map("ID" -> LexicalValue("7"),
+ "fname" -> LexicalValue("Bob"),
+ "addr" -> LexicalValue("18"),
+ "deptName" -> LexicalValue("accounting"),
+ "deptCity" -> LexicalValue("Cambridge")),
+ Map("ID" -> LexicalValue("8"),
+ "fname" -> LexicalValue("Sue"),
+ "addr" -> ␀(),
+ "deptName" -> ␀(),
+ "deptCity" -> ␀())), // no data
+ List(List("ID")),
+ Some(List("ID")),
+ Map(List("addr") -> Target("Addresses", List("ID")),
+ List("deptName", "deptCity") -> Target("Department", List("name", "city"))))
+
+ val department = Relation(Header(Map("ID" -> SQLInt(),
+ "name" -> SQLString(),
+ "city" -> SQLString(),
+ "manager" -> SQLInt())),
+ Set(Map("ID" -> LexicalValue("23"),
+ "name" -> LexicalValue("accounting"),
+ "city" -> LexicalValue("Cambridge"),
+ "manager" -> LexicalValue("8"))), // no data
+ List(List("ID"), List("name", "city")),
+ Some(List("ID")),
+ Map(List("manager") -> Target("People", List("ID"))))
+
+ val projects = Relation(Header(Map("lead" -> SQLInt(),
+ "name" -> SQLString(),
+ "deptName" -> SQLString(),
+ "deptCity" -> SQLString())),
+ Set(Map("lead" -> LexicalValue("8"),
+ "name" -> LexicalValue("pencil survey"),
+ "deptName" -> LexicalValue("accounting"),
+ "deptCity" -> LexicalValue("Cambridge")),
+ Map("lead" -> LexicalValue("8"),
+ "name" -> LexicalValue("eraser survey"),
+ "deptName" -> LexicalValue("accounting"),
+ "deptCity" -> LexicalValue("Cambridge"))),
+ List(List("lead", "name"), List("name", "deptName", "deptCity")),
+ None,
+ /* List(List("name"), List("lead", "name"), List("name", "deptName", "deptCity")),
+ List("name"), // !!! */
+ Map(List("lead") -> Target("People", List("ID")),
+ List("deptName", "deptCity") -> Target("Department", List("name", "city"))))
+
+ val tasks = Relation(Header(Map("worker" -> SQLInt(),
+ "project" -> SQLString(),
+ "deptName" -> SQLString(),
+ "deptCity" -> SQLString())),
+ Set(Map("worker" -> LexicalValue("7"),
+ "project" -> LexicalValue("pencil survey"),
+ "deptName" -> LexicalValue("accounting"),
+ "deptCity" -> LexicalValue("Cambridge"))),
+ List(List("worker", "project")),
+ Some(List("worker", "project")),
+ Map(List("worker") -> Target("People", List("ID")),
+ List("project", "deptName", "deptCity") -> Target("Projects", List("name", "deptName", "deptCity")),
+ List("deptName", "deptCity") -> Target("Department", List("name", "city"))))
+
+ val db = Database(Map("Addresses" -> addresses,
+ "People" -> people,
+ "Department" -> department,
+ "Projects" -> projects,
+ "TaskAssignments" -> tasks))
+ val g = databasemap(StemIRI("http://foo.example/DB"), db)
+
+ val expected:RDFGraph =
+ Set(
+ Triple(BNode("a"), IRI("http://foo.example/DB/Projects#lead"), IRI("http://foo.example/DB/People/ID.8#_")),
+ Triple(BNode("a"), IRI("http://foo.example/DB/Projects#name"), TypedLiteral("pencil survey", IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(BNode("a"), IRI("http://foo.example/DB/Projects#deptName"), TypedLiteral("accounting", IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(BNode("a"), IRI("http://foo.example/DB/Projects#deptCity"), TypedLiteral("Cambridge", IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(BNode("a"), IRI("http://foo.example/DB/Projects#deptName_deptCity"), IRI("http://foo.example/DB/Department/ID.23#_")),
+
+ Triple(BNode("b"), IRI("http://foo.example/DB/Projects#lead"), IRI("http://foo.example/DB/People/ID.8#_")),
+ Triple(BNode("b"), IRI("http://foo.example/DB/Projects#name"), TypedLiteral("eraser survey", IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(BNode("b"), IRI("http://foo.example/DB/Projects#deptName"), TypedLiteral("accounting", IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(BNode("b"), IRI("http://foo.example/DB/Projects#deptCity"), TypedLiteral("Cambridge", IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(BNode("b"), IRI("http://foo.example/DB/Projects#deptName_deptCity"), IRI("http://foo.example/DB/Department/ID.23#_")),
+
+ Triple(IRI("http://foo.example/DB/TaskAssignments/worker.7_project.pencil+survey#_"), IRI("http://foo.example/DB/TaskAssignments#worker"), IRI("http://foo.example/DB/People/ID.7#_")),
+ Triple(IRI("http://foo.example/DB/TaskAssignments/worker.7_project.pencil+survey#_"), IRI("http://foo.example/DB/TaskAssignments#project"), TypedLiteral("pencil survey", IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(IRI("http://foo.example/DB/TaskAssignments/worker.7_project.pencil+survey#_"), IRI("http://foo.example/DB/TaskAssignments#deptName"), TypedLiteral("accounting", IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(IRI("http://foo.example/DB/TaskAssignments/worker.7_project.pencil+survey#_"), IRI("http://foo.example/DB/TaskAssignments#deptCity"), TypedLiteral("Cambridge", IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(IRI("http://foo.example/DB/TaskAssignments/worker.7_project.pencil+survey#_"), IRI("http://foo.example/DB/TaskAssignments#deptName_deptCity"), IRI("http://foo.example/DB/Department/ID.23#_")),
+ Triple(IRI("http://foo.example/DB/TaskAssignments/worker.7_project.pencil+survey#_"), IRI("http://foo.example/DB/TaskAssignments#project_deptName_deptCity"), BNode("a")),
+
+ Triple(IRI("http://foo.example/DB/People/ID.7#_"),IRI("http://foo.example/DB/People#deptName"),TypedLiteral("accounting",IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(IRI("http://foo.example/DB/People/ID.7#_"),IRI("http://foo.example/DB/People#deptCity"),TypedLiteral("Cambridge",IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(IRI("http://foo.example/DB/People/ID.7#_"),IRI("http://foo.example/DB/People#deptName_deptCity"),IRI("http://foo.example/DB/Department/ID.23#_")),
+
+ Triple(IRI("http://foo.example/DB/Department/ID.23#_"),IRI("http://foo.example/DB/Department#ID"),TypedLiteral("23",IRI("http://www.w3.org/2001/XMLSchema#int"))),
+ Triple(IRI("http://foo.example/DB/Department/ID.23#_"),IRI("http://foo.example/DB/Department#name"),TypedLiteral("accounting",IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(IRI("http://foo.example/DB/Department/ID.23#_"),IRI("http://foo.example/DB/Department#city"),TypedLiteral("Cambridge",IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(IRI("http://foo.example/DB/Department/ID.23#_"),IRI("http://foo.example/DB/Department#manager"),IRI("http://foo.example/DB/People/ID.8#_")),
+
+ Triple(IRI("http://foo.example/DB/People/ID.7#_"),IRI("http://foo.example/DB/People#ID"),TypedLiteral("7",IRI("http://www.w3.org/2001/XMLSchema#int"))),
+ Triple(IRI("http://foo.example/DB/People/ID.7#_"),IRI("http://foo.example/DB/People#fname"),TypedLiteral("Bob",IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(IRI("http://foo.example/DB/People/ID.7#_"),IRI("http://foo.example/DB/People#addr"),IRI("http://foo.example/DB/Addresses/ID.18#_")),
+ Triple(IRI("http://foo.example/DB/People/ID.8#_"),IRI("http://foo.example/DB/People#ID"),TypedLiteral("8",IRI("http://www.w3.org/2001/XMLSchema#int"))),
+ Triple(IRI("http://foo.example/DB/People/ID.8#_"),IRI("http://foo.example/DB/People#fname"),TypedLiteral("Sue",IRI("http://www.w3.org/2001/XMLSchema#string"))),
+
+ Triple(IRI("http://foo.example/DB/Addresses/ID.18#_"),IRI("http://foo.example/DB/Addresses#ID"),TypedLiteral("18",IRI("http://www.w3.org/2001/XMLSchema#int"))),
+ Triple(IRI("http://foo.example/DB/Addresses/ID.18#_"),IRI("http://foo.example/DB/Addresses#city"),TypedLiteral("Cambridge",IRI("http://www.w3.org/2001/XMLSchema#string"))),
+ Triple(IRI("http://foo.example/DB/Addresses/ID.18#_"),IRI("http://foo.example/DB/Addresses#state"),TypedLiteral("MA",IRI("http://www.w3.org/2001/XMLSchema#string")))
+ )
+ assert (expected === g)
+ }
+
+
test("2 Employees") {
val employees = Relation(Header(Map("ID" -> SQLInt(),
"fname" -> SQLString(),
@@ -137,7 +267,7 @@
"boss" -> LexicalValue("1"))
),
List(List("ID")),
- List("ID"),
+ Some(List("ID")),
Map(List("boss") -> Target("Employees", List("ID"))))
val db = Database(Map("Employees" -> employees))
val g = databasemap(StemIRI("http://foo.example/DB"), db)