Joins
parent
f693ca0e61
commit
8373b53621
|
@ -0,0 +1,5 @@
|
|||
d,e,f
|
||||
1,2,3
|
||||
4,5,6
|
||||
1,3,9
|
||||
2,5,8
|
|
|
@ -176,7 +176,7 @@ object Expression {
|
|||
def eval(scope: Map[Variable, Constant]): Constant =
|
||||
Constant.Boolean(a.eval(scope) == b.eval(scope))
|
||||
|
||||
override def printString = s"$a = $b"
|
||||
override def printString = s"${a.printString} = ${b.printString}"
|
||||
def children: Seq[Expression] = Seq(a, b)
|
||||
def rebuild(c: Seq[Expression]): Expression = copy(a = c(0), b = c(1))
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ object Expression {
|
|||
}
|
||||
)
|
||||
|
||||
override def printString = s"$a AND $b"
|
||||
override def printString = s"${a.printString} AND ${b.printString}"
|
||||
def children: Seq[Expression] = Seq(a, b)
|
||||
def rebuild(c: Seq[Expression]): Expression = copy(a = c(0), b = c(1))
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ object Expression {
|
|||
}
|
||||
)
|
||||
|
||||
override def printString = s"$a AND $b"
|
||||
override def printString = s"${a.printString} OR ${b.printString}"
|
||||
def children: Seq[Expression] = Seq(a, b)
|
||||
def rebuild(c: Seq[Expression]): Expression = copy(a = c(0), b = c(1))
|
||||
}
|
||||
|
|
|
@ -15,4 +15,9 @@ case class Filter(predicate: Expression, child: Plan) extends Plan
|
|||
case _ => throw new Exception(s"Invalid predicate: ${predicate.printString}")
|
||||
}
|
||||
}
|
||||
|
||||
override def planParameterString: String = predicate.printString
|
||||
|
||||
def children: Seq[Plan] = Seq(child)
|
||||
def rebuild(c: Seq[Plan]): Plan = copy(child = c(0))
|
||||
}
|
|
@ -28,10 +28,11 @@ object HackDB
|
|||
query("SELECT 5*4")
|
||||
query("SELECT 'foo'")
|
||||
query("SELECT 'foo'+'bar'")
|
||||
query("SELECT 5*4 FROM test.csv")
|
||||
query("SELECT a, b FROM test.csv")
|
||||
query("SELECT a, b, a+b FROM test.csv")
|
||||
query("SELECT a, b, a+b FROM test.csv WHERE a = 1")
|
||||
query("SELECT a, b, a+b FROM test.csv WHERE a = 1 OR a = 2")
|
||||
query("SELECT 5*4 FROM test")
|
||||
query("SELECT a, b FROM test")
|
||||
query("SELECT a, b, a+b FROM test")
|
||||
query("SELECT a, b, a+b FROM test WHERE a = 1")
|
||||
query("SELECT a, b, a+b FROM test WHERE a = 1 OR a = 2")
|
||||
query("SELECT a, b, f FROM test, best")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package net.okennedy.hackdb
|
||||
|
||||
case class Join(
|
||||
left: Plan,
|
||||
right: Plan,
|
||||
predicate: Option[(Variable, Variable)] = None
|
||||
) extends Plan
|
||||
{
|
||||
def schema: Seq[(String, Type)] = left.schema ++ right.schema
|
||||
def read: Iterator[Seq[Constant]] =
|
||||
{
|
||||
predicate match {
|
||||
case None =>
|
||||
{
|
||||
val leftBuffer = left.read.toSeq
|
||||
right.read.flatMap { right =>
|
||||
leftBuffer.map { left =>
|
||||
left ++ right
|
||||
}
|
||||
}
|
||||
}
|
||||
case Some(a, b) =>
|
||||
{
|
||||
???
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override def planParameterString: String =
|
||||
predicate.match {
|
||||
case None => "CROSS PRODUCT"
|
||||
case Some((a, b)) => s"${a.name} = ${b.name}"
|
||||
}
|
||||
|
||||
def children: Seq[Plan] = Seq(left, right)
|
||||
def rebuild(c: Seq[Plan]): Plan = copy(left = c(0), right = c(1))
|
||||
}
|
|
@ -9,6 +9,11 @@ object Parser
|
|||
|
||||
|
||||
def ws[$: P]: P[Unit] = CharIn(" \r\n\t").rep
|
||||
|
||||
def identifier[$: P]: P[Unit] = CharIn("a-zA-Z") ~ CharIn("a-zA-Z0-9").rep
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////// Query Bits ////////////////////////
|
||||
def query[$: P]: P[Query] = P(
|
||||
|
@ -16,24 +21,31 @@ object Parser
|
|||
expressionList ~ ws ~
|
||||
from.? ~ ws ~
|
||||
where.?
|
||||
).map { (exprs, table, predicate) =>
|
||||
Query(exprs = exprs, table = table, predicate = predicate)
|
||||
).map { (exprs, tables, predicate) =>
|
||||
Query(
|
||||
exprs = exprs,
|
||||
tables = tables.getOrElse { Seq.empty },
|
||||
predicate = predicate
|
||||
)
|
||||
}
|
||||
|
||||
def from[$: P]: P[Table] = P(
|
||||
IgnoreCase("from") ~/ ws ~
|
||||
table
|
||||
)
|
||||
|
||||
def where[$: P]: P[Expression] = P(
|
||||
IgnoreCase("where") ~/ ws ~
|
||||
expression
|
||||
)
|
||||
|
||||
def from[$: P]: P[Seq[Table]] = P(
|
||||
IgnoreCase("from") ~/ ws ~
|
||||
tableList
|
||||
)
|
||||
|
||||
def tableList[$: P]: P[Seq[Table]] = P(
|
||||
table ~ ws ~ ("," ~/ ws ~ table ~ ws).rep
|
||||
).map { (head, tail) => head +: tail }
|
||||
|
||||
def table[$: P]: P[Table] = P(
|
||||
(
|
||||
((!CharIn(". ")) ~ AnyChar).rep(1) ~ ".".! ~
|
||||
((!CharIn(". ")) ~ AnyChar).rep(1)
|
||||
identifier.rep(1)
|
||||
).!.map { Table.fromFile(_) }
|
||||
)
|
||||
|
||||
|
|
|
@ -4,4 +4,35 @@ trait Plan
|
|||
{
|
||||
def schema: Seq[(String, Type)]
|
||||
def read: Iterator[Seq[Constant]]
|
||||
|
||||
def children: Seq[Plan]
|
||||
def rebuild(c: Seq[Plan]): Plan
|
||||
|
||||
def transformDown(op: PartialFunction[Plan, Plan]): Plan =
|
||||
{
|
||||
val rewritten = op.applyOrElse(this, _ => this)
|
||||
|
||||
rewritten.rebuild(
|
||||
rewritten.children.map { _.transformDown(op) }
|
||||
)
|
||||
}
|
||||
|
||||
def transformUp(op: PartialFunction[Plan, Plan]): Plan =
|
||||
{
|
||||
val rewritten = rebuild(
|
||||
children.map { _.transformDown(op) }
|
||||
)
|
||||
|
||||
op.applyOrElse(rewritten, _ => rewritten)
|
||||
}
|
||||
|
||||
def planParameterString = ""
|
||||
|
||||
def toString(prefix: String): String =
|
||||
prefix + this.getClass.getSimpleName() + "(" + planParameterString + ")" +
|
||||
children.map { child => "\n" + child.toString(prefix+" ")}.mkString
|
||||
|
||||
override def toString(): String =
|
||||
toString("")
|
||||
|
||||
}
|
|
@ -11,4 +11,10 @@ case class Project(exprs: Seq[Expression], child: Plan) extends Plan
|
|||
child.read.map { row =>
|
||||
exprs.map { _.eval(vars.map { _._1 }.zip(row).toMap) }
|
||||
}
|
||||
|
||||
override def planParameterString: String =
|
||||
exprs.map { _.printString }.mkString(", ")
|
||||
|
||||
def children: Seq[Plan] = Seq(child)
|
||||
def rebuild(c: Seq[Plan]): Plan = copy(child = c(0))
|
||||
}
|
|
@ -2,22 +2,27 @@ package net.okennedy.hackdb
|
|||
|
||||
case class Query(
|
||||
exprs: Seq[Expression],
|
||||
table: Option[Table],
|
||||
tables: Seq[Table],
|
||||
predicate: Option[Expression]
|
||||
)
|
||||
{
|
||||
lazy val plan: Plan =
|
||||
{
|
||||
var ret:Plan =
|
||||
table.getOrElse { Table(Seq(), Seq(Seq())) }
|
||||
if(tables.isEmpty){
|
||||
Table(Seq(), Seq(Seq()))
|
||||
} else {
|
||||
tables.tail.foldLeft(tables.head:Plan) { Join(_, _) }
|
||||
}
|
||||
|
||||
ret = Project(exprs, ret)
|
||||
|
||||
ret =
|
||||
predicate.map { Filter(_, ret) }
|
||||
.getOrElse { ret }
|
||||
|
||||
// println(ret.toString)
|
||||
ret = Project(exprs, ret)
|
||||
|
||||
|
||||
println(ret.toString)
|
||||
|
||||
/* return */ ret
|
||||
}
|
||||
|
|
|
@ -6,6 +6,11 @@ case class Table(schema: Seq[(String, Type)], data: Seq[Seq[Constant]]) extends
|
|||
{
|
||||
def read: Iterator[Seq[Constant]] =
|
||||
data.iterator
|
||||
|
||||
override def planParameterString: String = schema.map { (name, t) => s"$name: $t" }.mkString(", ")
|
||||
|
||||
def children: Seq[Plan] = Seq()
|
||||
def rebuild(c: Seq[Plan]): Plan = this
|
||||
}
|
||||
|
||||
object Table
|
||||
|
@ -13,7 +18,7 @@ object Table
|
|||
def fromFile(file: String): Table =
|
||||
{
|
||||
val contents =
|
||||
Source.fromFile(file)
|
||||
Source.fromFile(file+".csv")
|
||||
.getLines()
|
||||
.map { _.split(",") }
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ object Type
|
|||
def of(expr: Expression, scope: Map[Variable, Type]): Type =
|
||||
expr match {
|
||||
case c:Constant => c.dataType
|
||||
case a:Variable => scope(a)
|
||||
case a:Variable => scope.get(a).getOrElse { throw new Exception(s"Can't find ${a.name} in ${scope.keys.map { _.name }.mkString(", ")}")}
|
||||
case Expression.Add(a, b) => assertIn(Type.Int, Type.Double, Type.String)(
|
||||
assertSame(of(a, scope), of(b, scope)))
|
||||
case Expression.Sub(a, b) => assertIn(Type.Int, Type.Double)(
|
||||
|
|
Loading…
Reference in New Issue