main
Oliver Kennedy 2023-11-02 12:26:54 -04:00
parent f693ca0e61
commit 8373b53621
Signed by: okennedy
GPG Key ID: 3E5F9B3ABD3FDB60
11 changed files with 132 additions and 24 deletions

5
best.csv Normal file
View File

@ -0,0 +1,5 @@
d,e,f
1,2,3
4,5,6
1,3,9
2,5,8
1 d e f
2 1 2 3
3 4 5 6
4 1 3 9
5 2 5 8

View File

@ -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))
}

View File

@ -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))
}

View File

@ -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")
}
}

View File

@ -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))
}

View File

@ -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(_) }
)

View File

@ -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("")
}

View File

@ -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))
}

View File

@ -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
}

View File

@ -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(",") }

View File

@ -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)(