--- a/src/main/scala/SQL.scala Sat Feb 27 00:26:57 2010 -0500
+++ b/src/main/scala/SQL.scala Sat Feb 27 08:46:18 2010 -0500
@@ -169,11 +169,16 @@
sealed abstract class KeyDeclaration extends FieldDescOrKeyDeclaration
case class PrimaryKeyDeclaration(attr:Attribute) extends KeyDeclaration
case class ForeignKeyDeclaration(fk:Attribute, rel:Relation, pk:Attribute) extends KeyDeclaration
+case class View(rel:Relation, defn:SelectORUnion) // sibling of RelationDesc
case class Sql() extends JavaTokenParsers {
+ def createview:Parser[View] = // @@@ could stick under ddl
+ "CREATE" ~ "VIEW" ~ relation ~ "AS" ~ selectORunion ^^
+ { case "CREATE"~"VIEW"~relation~"AS"~defn => View(relation, defn) }
+
def ddl:Parser[DatabaseDesc] =
- rep1sep(create, ";") ~ opt(";") ^^
+ rep1sep(createtable, ";") ~ opt(";") ^^
{
case l~x => DatabaseDesc(l.foldLeft(Map[Relation, RelationDesc]())((m, p) => {
val (rel:Relation, desc:RelationDesc) = p
@@ -181,13 +186,17 @@
}))
}
- def create:Parser[(Relation, RelationDesc)] =
+ def createtable:Parser[(Relation, RelationDesc)] =
"CREATE" ~ "TABLE" ~ relation ~ "(" ~ rep1sep(fielddescorkeydef, ",") ~ ")" ^^
{
case "CREATE"~"TABLE"~relation~"("~reldesc~")" => {
val pk0:Option[Attribute] = None
val attrs0 = Map[Attribute, ValueDescription]()
val fks0 = Map[Attribute, 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, fks) =
reldesc.foldLeft((pk0, attrs0, fks0))((p, rd) => {
val (pkopt, attrs, fks) = p
@@ -198,10 +207,15 @@
else pkopt
(pkNew, attrs + (attr -> value), fks)
}
- case PrimaryKeyDeclaration(attr) => (Some(attr), attrs, fks)
- case ForeignKeyDeclaration(fk, rel, pk) => (pkopt, attrs, fks + (fk -> ForeignKey(rel, pk)))
+ case PrimaryKeyDeclaration(attr) =>
+ (Some(attr), attrs, fks)
+ case ForeignKeyDeclaration(fk, rel, pk) =>
+ (pkopt, attrs, fks + (fk -> ForeignKey(rel, pk)))
}
})
+ /* Change data type of foreign key attributes to ForeignKey.
+ * The type isn't really lost -- it's the same as the referenced type.
+ */
val attrs2 = attrs.map(x => {
val (attr:Attribute, value:Value) = x
if (fks.contains(attr)) (attr -> fks(attr))
--- a/src/test/scala/SQLTest.scala Sat Feb 27 00:26:57 2010 -0500
+++ b/src/test/scala/SQLTest.scala Sat Feb 27 08:46:18 2010 -0500
@@ -389,7 +389,7 @@
(Relation("Sex_DE") ->
RelationDesc(Option(Attribute("ID")),
Map(Attribute("ID") -> Value(Datatype.INTEGER))))
- assert(expected === (a.parseAll(a.create, e).get))
+ assert(expected === (a.parseAll(a.createtable, e).get))
}
test("integrated PK") {
@@ -486,4 +486,33 @@
assert(expected === (a.parseAll(a.ddl, e).get))
}
+ test("CREATE VIEW") {
+ // AliasedResource(Relation(Name("Employee")),RelVar(Name("R_emp")))
+ val a = Sql()
+ val e = """
+CREATE VIEW triples AS SELECT
+ CONCAT("http://hr.example/DB/", "Employee", "/", "empid", ".", R_emp.id, "#record") AS S,
+ "<http://hr.example/DB/Employee#lastName>" AS P,
+ R_emp.lastName AS O
+ FROM Employee AS R_emp
+""" // "
+ val expected = View(Relation(Name("triples")), Select(AttributeList(Set(
+ NamedAttribute(Concat(List(PrimaryExpressionTyped(Datatype("String"),Name("http://hr.example/DB/")),
+ PrimaryExpressionTyped(Datatype("String"),Name("Employee")),
+ PrimaryExpressionTyped(Datatype("String"),Name("/")),
+ PrimaryExpressionTyped(Datatype("String"),Name("empid")),
+ PrimaryExpressionTyped(Datatype("String"),Name(".")),
+ PrimaryExpressionAttr(RelVarAttr(RelVar(Name("R_emp")),Attribute(Name("id")))),
+ PrimaryExpressionTyped(Datatype("String"),Name("#record")))),
+ AttrAlias(Name("S"))),
+ NamedAttribute(PrimaryExpressionTyped(Datatype.STRING,Name("<http://hr.example/DB/Employee#lastName>")),
+ AttrAlias(Name("P"))),
+ NamedAttribute(RelVarAttr(RelVar(Name("R_emp")),
+ Attribute(Name("lastName"))),
+ AttrAlias(Name("O"))))),
+ TableList(AddOrderedSet(InnerJoin(AliasedResource(Relation(Name("Employee")),RelVar(Name("R_emp"))), None))),
+ None))
+ assert(expected === (a.parseAll(a.createview, e).get))
+ }
+
}