Skip to content

Commit

Permalink
refactored binary operators
Browse files Browse the repository at this point in the history
  • Loading branch information
hkarim committed Jan 17, 2018
1 parent 9f34a2c commit 4716fa8
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 113 deletions.
38 changes: 26 additions & 12 deletions jam-core/src/main/scala/jam/sql/Backend.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ trait Backend[DBF[_], R[_], W[_]]
case PropertyName(p) => const(ns.name(p))
case PropertyAliasNode(a, v) => const(s"${a.name}.") |+| run(PropertyName(v))
case SubstitutedExpression(a, _) => const(a.name)
case EncloseExpression(v) => const("(") |+| run(v) |+| const(")")
case AsNode(a, v, t) => run(t(v)) |+| const(" as ") |+| const(a.name)
case p: Property[_] => run(PropertyName(p))
case c: Composite[_] => c.properties.vector.map(PropertyName).map(run).sep(comma)
Expand Down Expand Up @@ -70,27 +71,39 @@ trait Backend[DBF[_], R[_], W[_]]
case UnionNode(p, UnionType.UnionAll, v, t) => run(p) |+| const(" union all ") |+| run(t(v))

case DeleteFromNode(DML, v, t) => const("delete from ") |+| run(t(v))

case InsertIntoNode(DML, v, t) => const("insert into ") |+| run(t(v))
case ValuesNode(p, vs, t) => run(p) |+| const(" values ") |+| vs.map(v => run(t(v)).enclose).sep(comma)
case InsertIntoSelect(p, sn) => run(p) |+| const(" ") |+| run(sn)

case UpdateNode(DML, v, t) => const("update ") |+| run(t(v))
case SetNode(p, vs, t) => run(p) |+| const(" set ") |+| vs.map(v => run(t(v))).sep(comma)

case DMLWhereNode(p, v, t) => run(p) |+| const(" where ") |+| run(t(v))

case Eq(l, r) => run(l) |+| const(" = ") |+| run(r)
case Ne(l, r) => run(l) |+| const(" <> ") |+| run(r)
case Lt(l, r) => run(l) |+| const(" < ") |+| run(r)
case Le(l, r) => run(l) |+| const(" <= ") |+| run(r)
case Gt(l, r) => run(l) |+| const(" > ") |+| run(r)
case Ge(l, r) => run(l) |+| const(" >= ") |+| run(r)
case ArithmeticOperator.Plus => const(" + ")
case ArithmeticOperator.Minus => const(" - ")
case ArithmeticOperator.Multiply => const(" * ")
case ArithmeticOperator.Divide => const(" / ")

case EqOperator.Eq => const(" = ")
case EqOperator.Ne => const(" <> ")

case PartialOrderOperator.Gt => const(" > ")
case PartialOrderOperator.Ge => const(" >= ")
case PartialOrderOperator.Lt => const(" < ")
case PartialOrderOperator.Le => const(" <= ")

case LogicOperator.And => const(" and ")
case LogicOperator.Or => const(" or ")

case InfixNode(op@LogicOperator.And, l, r) => run(l).enclose |+| run(op) |+| run(r).enclose
case InfixNode(op@LogicOperator.Or, l, r) => run(l).enclose |+| run(op) |+| run(r).enclose
case InfixNode(op, l, r) => run(l) |+| run(op) |+| run(r)

case CEq(l, r) => run(l).enclose |+| const(" = ") |+| run(r).enclose
case CNe(l, r) => run(l).enclose |+| const(" <> ") |+| run(r).enclose

case And(l, r) => run(l).enclose |+| const(" and ") |+| run(r).enclose
case Or(l, r) => run(l).enclose |+| const(" or ") |+| run(r).enclose

case Not(e) => const("not ") |+| run(e).enclose
case NotNode(e) => const("not ") |+| run(e).enclose

case InNode(l, r, not) =>
(l, r.toList) match {
Expand All @@ -102,7 +115,8 @@ trait Backend[DBF[_], R[_], W[_]]
run(l) |+| (if (not) const(" not in ") else const(" in ")) |+| r.map(run).sep(comma).enclose
}

case Like(l, r) => run(l) |+| const(" like ") |+| run(r)
case LikeNode(l, r, not) =>
run(l) |+| (if(not) const(" not like ") else const(" like ")) |+| run(r)

case AscNode(e) => run(e) |+| const(" asc")
case DescNode(e) => run(e) |+| const(" desc")
Expand Down
53 changes: 34 additions & 19 deletions jam-core/src/main/scala/jam/sql/ModelSyntax.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,44 @@ trait ModelSyntax {

def property[P](name: String): Property[P] = Property.Strict[P](name)

implicit class BooleanOps(l: Expression[Boolean]) {
def and(r: Expression[Boolean]): Expression[Boolean] = And(l, r)
def or(r: Expression[Boolean]): Expression[Boolean] = Or(l, r)
}
def not(e: Expression[Boolean]): Expression[Boolean] = Not(e)

implicit class EquatableOps[L[_], A](l: L[A]) {
def ===[R[_]](r: R[A])(implicit ev: Equatable[L, R]): Expression[Boolean] = ev.eq(l, r)
def =!=[R[_]](r: R[A])(implicit ev: Equatable[L, R]): Expression[Boolean] = ev.neq(l, r)
}

implicit class ComparableOps[L[_] <: Expression[_], A](l: L[A]) {
def >[R[_] <: Expression[_]](r: R[A])(implicit ev: Comparable[A]): Expression[Boolean] = ev.gt(l, r)
def >=[R[_] <: Expression[_]](r: R[A])(implicit ev: Comparable[A]): Expression[Boolean] = ev.ge(l, r)
def <[R[_] <: Expression[_]](r: R[A])(implicit ev: Comparable[A]): Expression[Boolean] = ev.lt(l, r)
def <=[R[_] <: Expression[_]](r: R[A])(implicit ev: Comparable[A]): Expression[Boolean] = ev.le(l, r)
}

implicit class ExpressionOps[A](l: Expression[A]) {
def in(first: Expression[A], rest: Expression[A]*): Expression[Boolean] = InNode(l, first +: rest, negate = false)
def notIn(first: Expression[A], rest: Expression[A]*): Expression[Boolean] = InNode(l, first +: rest, negate = true)
def like(e: Expression[A]): Expression[Boolean] = Like(l, e)
def like(e: Expression[A]): Expression[Boolean] = LikeNode(l, e, negate = false)
def notLike(e: Expression[A]): Expression[Boolean] = LikeNode(l, e, negate = true)

def +(r: Expression[A])(implicit ev: Infix[ArithmeticOperator.Plus.type, Expression, Expression, A, A]): Expression[A] =
ev(ArithmeticOperator.Plus, l, r)
def -(r: Expression[A])(implicit ev: Infix[ArithmeticOperator.Minus.type, Expression, Expression, A, A]): Expression[A] =
ev(ArithmeticOperator.Minus, l, r)
def *(r: Expression[A])(implicit ev: Infix[ArithmeticOperator.Multiply.type, Expression, Expression, A, A]): Expression[A] =
ev(ArithmeticOperator.Multiply, l, r)
def /(r: Expression[A])(implicit ev: Infix[ArithmeticOperator.Divide.type, Expression, Expression, A, A]): Expression[A] =
ev(ArithmeticOperator.Divide, l, r)

def ===(r: Expression[A])(implicit ev: Infix[EqOperator.Eq.type, Expression, Expression, A, Boolean]): Expression[Boolean] =
ev(EqOperator.Eq, l, r)
def =!=(r: Expression[A])(implicit ev: Infix[EqOperator.Ne.type, Expression, Expression, A, Boolean]): Expression[Boolean] =
ev(EqOperator.Ne, l, r)

def >(r: Expression[A])(implicit ev: Infix[PartialOrderOperator.Gt.type, Expression, Expression, A, Boolean]): Expression[Boolean] =
ev(PartialOrderOperator.Gt, l, r)
def >=(r: Expression[A])(implicit ev: Infix[PartialOrderOperator.Ge.type, Expression, Expression, A, Boolean]): Expression[Boolean] =
ev(PartialOrderOperator.Ge, l, r)
def <(r: Expression[A])(implicit ev: Infix[PartialOrderOperator.Lt.type, Expression, Expression, A, Boolean]): Expression[Boolean] =
ev(PartialOrderOperator.Lt, l, r)
def <=(r: Expression[A])(implicit ev: Infix[PartialOrderOperator.Le.type, Expression, Expression, A, Boolean]): Expression[Boolean] =
ev(PartialOrderOperator.Le, l, r)

}

implicit class BooleanOps(l: Expression[Boolean]) {
def and(r: Expression[Boolean])(implicit ev: Infix[LogicOperator.And.type, Expression, Expression, Boolean, Boolean]): Expression[Boolean] =
ev(LogicOperator.And, l, r)
def or(r: Expression[Boolean])(implicit ev: Infix[LogicOperator.Or.type, Expression, Expression, Boolean, Boolean]): Expression[Boolean] =
ev(LogicOperator.Or, l, r)
}
def not(e: Expression[Boolean]): Expression[Boolean] = NotNode(e)

implicit class PropertiesOps[In <: HList, Out <: HList, LOut <: HList, ZOut <: HList](out: Out) {
def properties[A](implicit g: Generic.Aux[A, In],
Expand Down
47 changes: 35 additions & 12 deletions jam-core/src/main/scala/jam/sql/sql-ast.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import cats.data.NonEmptyList
trait Node

trait Expression[+A] extends Node { self =>
def enclose: Expression[A] = EncloseExpression[A](this)
def ~(alias: Symbol): Expression[A] = SubstitutedExpression(alias, self)
}
case class ExpressionList[H](expressions: Vector[Expression[_]]) extends Expression[H]
Expand Down Expand Up @@ -221,6 +222,7 @@ trait DMLWhere extends Where
case class EntityName(value: Entity[_]) extends Node
case class PropertyName(value: Property[_]) extends Node
case class SubstitutedExpression[A](alias: Symbol, value: Expression[A]) extends Expression[A]
case class EncloseExpression[A](value: Expression[A]) extends Expression[A]

case class PropertyAliasNode[A](alias: Symbol, value: Property[A]) extends Expression[A] {
def asc: OrderLikeNode[A] = AscNode(this)
Expand Down Expand Up @@ -316,21 +318,42 @@ case class SetNode[A](parent: Node, value: TraversableOnce[A], ts: TSet[A]) exte

case class DeleteFromNode[F[_], A](parent: Node, value: F[A], tf: TFrom[F, A]) extends Node with HasDMLWhere

case class Eq[L[_] <: Expression[_], R[_] <: Expression[_], A](lhs: L[A], rhs: R[A]) extends Expression[Boolean]
case class Ne[L[_] <: Expression[_], R[_] <: Expression[_], A](lhs: L[A], rhs: R[A]) extends Expression[Boolean]
case class Lt[L[_] <: Expression[_], R[_] <: Expression[_], A](lhs: L[A], rhs: R[A]) extends Expression[Boolean]
case class Le[L[_] <: Expression[_], R[_] <: Expression[_], A](lhs: L[A], rhs: R[A]) extends Expression[Boolean]
case class Gt[L[_] <: Expression[_], R[_] <: Expression[_], A](lhs: L[A], rhs: R[A]) extends Expression[Boolean]
case class Ge[L[_] <: Expression[_], R[_] <: Expression[_], A](lhs: L[A], rhs: R[A]) extends Expression[Boolean]
trait BinaryOperator extends Node

sealed trait ArithmeticOperator extends BinaryOperator
object ArithmeticOperator {
case object Plus extends ArithmeticOperator
case object Minus extends ArithmeticOperator
case object Multiply extends ArithmeticOperator
case object Divide extends ArithmeticOperator
}

sealed trait EqOperator extends BinaryOperator
object EqOperator {
case object Eq extends EqOperator
case object Ne extends EqOperator
}

sealed trait PartialOrderOperator extends BinaryOperator
object PartialOrderOperator {
case object Gt extends PartialOrderOperator
case object Ge extends PartialOrderOperator
case object Lt extends PartialOrderOperator
case object Le extends PartialOrderOperator
}

sealed trait LogicOperator extends BinaryOperator
object LogicOperator {
case object And extends LogicOperator
case object Or extends LogicOperator
}

case class InfixNode[A, T](operator: BinaryOperator, lhs: Expression[A], rhs: Expression[A]) extends Expression[T]

case class CEq[L[_] <: Expression[_], R[_] <: Expression[_], A](lhs: L[A], rhs: R[A]) extends Expression[Boolean]
case class CNe[L[_] <: Expression[_], R[_] <: Expression[_], A](lhs: L[A], rhs: R[A]) extends Expression[Boolean]

case class InNode[A](lhs: Expression[A], rhs: TraversableOnce[Expression[A]], negate: Boolean) extends Expression[Boolean]

case class Like[A](lhs: Expression[A], rhs: Expression[A]) extends Expression[Boolean]
case class LikeNode[A](lhs: Expression[A], rhs: Expression[A], negate: Boolean) extends Expression[Boolean]

case class And(lhs: Expression[Boolean], rhs: Expression[Boolean]) extends Expression[Boolean]
case class Or(lhs: Expression[Boolean], rhs: Expression[Boolean]) extends Expression[Boolean]

case class Not(e: Expression[Boolean]) extends Expression[Boolean]
case class NotNode(e: Expression[Boolean]) extends Expression[Boolean]
74 changes: 23 additions & 51 deletions jam-core/src/main/scala/jam/sql/sql-model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,64 +48,36 @@ object Validator {
}
}

trait Equatable[L[_], R[_]] {
def eq[A](l: L[A], r: R[A]): Expression[Boolean]
def neq[A](l: L[A], r: R[A]): Expression[Boolean]
}
trait EquatableLowPriorityInstances {
implicit def swap[L[_], R[_]](implicit ev: Equatable[R, L]): Equatable[L, R] = new Equatable[L, R] {
def eq[A](l: L[A], r: R[A]): Expression[Boolean] = ev.eq(r, l)
def neq[A](l: L[A], r: R[A]): Expression[Boolean] = ev.neq(r, l)
}
trait Prefix[-O, -F[_], A, +T] {
def apply(operator: O, f: F[A]): Expression[T]
}
object Equatable extends EquatableLowPriorityInstances {

implicit def property[P[_] <: Property[_], E[_] <: Expression[_]]: Equatable[P, E] = new Equatable[P, E] {
def eq[A](l: P[A], r: E[A]): Expression[Boolean] = Eq(l, r)
def neq[A](l: P[A], r: E[A]): Expression[Boolean] = Ne(l, r)
}

implicit def composite[C[_] <: Composite[_], E[_] <: Expression[_]]: Equatable[C, E] = new Equatable[C, E] {
def eq[A](l: C[A], r: E[A]): Expression[Boolean] = CEq(l, r)
def neq[A](l: C[A], r: E[A]): Expression[Boolean] = CNe(l, r)
}

implicit def alias[N[_] <: PropertyAliasNode[_], E[_] <: Expression[_]]: Equatable[N, E] = new Equatable[N, E] {
def eq[A](l: N[A], r: E[A]): Expression[Boolean] = Eq(l, r)
def neq[A](l: N[A], r: E[A]): Expression[Boolean] = Ne(l, r)
}
trait PrefixLowPriorityInstances {

}

trait ValueType[A, B] {
def generic: Generic.Aux[A, B :: HNil]
def valueOf(a: A): B = generic.to(a).head
def toValueType(b: B): A = generic.from(b :: HNil)
object Prefix {
def apply[O, F[_], A, T](ev: Prefix[O, F, A, T]): Prefix[O, F, A, T] = ev
}

trait Comparable[A] {
def gt[L[_] <: Expression[_], R[_] <: Expression[_]](l: L[A], r: R[A]): Expression[Boolean]
def ge[L[_] <: Expression[_], R[_] <: Expression[_]](l: L[A], r: R[A]): Expression[Boolean]
def lt[L[_] <: Expression[_], R[_] <: Expression[_]](l: L[A], r: R[A]): Expression[Boolean]
def le[L[_] <: Expression[_], R[_] <: Expression[_]](l: L[A], r: R[A]): Expression[Boolean]
trait Infix[-O, -L[_], -R[_], A, +T] {
def apply(operator: O, l: L[A], r: R[A]): Expression[T]
}
trait ComparableLowPriorityInstances {
implicit def numeric[A: Numeric]: Comparable[A] = new Comparable[A] {
def gt[L[_] <: Expression[_], R[_] <: Expression[_]](l: L[A], r: R[A]): Expression[Boolean] = Gt(l, r)
def ge[L[_] <: Expression[_], R[_] <: Expression[_]](l: L[A], r: R[A]): Expression[Boolean] = Ge(l, r)
def lt[L[_] <: Expression[_], R[_] <: Expression[_]](l: L[A], r: R[A]): Expression[Boolean] = Lt(l, r)
def le[L[_] <: Expression[_], R[_] <: Expression[_]](l: L[A], r: R[A]): Expression[Boolean] = Le(l, r)
}
trait InfixLowPriorityInstances {
implicit def arithmeticOperator[A: Numeric]: Infix[ArithmeticOperator, Expression, Expression, A, A] =
Infix.instance(InfixNode[A, A])

implicit def eqOperator[A]: Infix[EqOperator, Expression, Expression, A, Boolean] =
Infix.instance(InfixNode[A, Boolean])

implicit def partialOrderOperator[A]: Infix[PartialOrderOperator, Expression, Expression, A, Boolean] =
Infix.instance(InfixNode[A, Boolean])

implicit val logicOperator: Infix[LogicOperator, Expression, Expression, Boolean, Boolean] =
Infix.instance(InfixNode[Boolean, Boolean])
}
object Comparable extends ComparableLowPriorityInstances {
def apply[A](ev: Comparable[A]): Comparable[A] = ev
def instance[A, B: Comparable](implicit g: Generic.Aux[A, B :: HNil]): Comparable[A] = new Comparable[A] with ValueType[A, B] {
def generic: Generic.Aux[A, B :: HNil] = g // just to get rid of the compiler warning
def gt[L[_] <: Expression[_], R[_] <: Expression[_]](l: L[A], r: R[A]): Expression[Boolean] = Gt(l, r)
def ge[L[_] <: Expression[_], R[_] <: Expression[_]](l: L[A], r: R[A]): Expression[Boolean] = Ge(l, r)
def lt[L[_] <: Expression[_], R[_] <: Expression[_]](l: L[A], r: R[A]): Expression[Boolean] = Lt(l, r)
def le[L[_] <: Expression[_], R[_] <: Expression[_]](l: L[A], r: R[A]): Expression[Boolean] = Le(l, r)
}
object Infix extends InfixLowPriorityInstances {
def apply[O, L[_], R[_], A, T](implicit ev: Infix[O, L, R, A, T]): Infix[O, L, R, A, T] = ev
def instance[O, L[_], R[_], A, T](f: (O, L[A], R[A]) => Expression[T]): Infix[O, L, R, A, T] =
(operator: O, l: L[A], r: R[A]) => f(operator, l, r)
}

trait Write[A] {
Expand Down
53 changes: 34 additions & 19 deletions jam-example/src/main/scala/jam/example/e000/00-start-here.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ object Model {

implicit val ns: NamingStrategy = NamingStrategy.Postgres // or MySQL

def findCountry[F[_]: Jam : Functor](name: String)(implicit w: Write[String], r: Read[F, Country]): F[Option[Country]] =
def findCountry[F[_]: Jam : Functor](name: String)
(implicit
ws: Write[String],
ll: Literal[Long],
r: Read[F, Country]): F[Option[Country]] =
DQL
.from(c)
.where(c.name === name.param)
.where(c.name === name.param and (c.population - 100L.literal) > 200L.literal)
.select(c)
.query
.map(_.headOption)
Expand Down Expand Up @@ -69,6 +73,23 @@ object Main {

findCountry2("Egypt".param).query.map(_.headOption).transact(xa).unsafeRunSync()



findCountry[ConnectionIO]("Egypt").transact(xa).unsafeToFuture()
}

def slick: Future[Option[Country]] = {
import jam.slick.implicits._
import jam.slick.jdbcProfile.api._

Class.forName("org.postgresql.Driver")

val db: Database = Database.forURL(
url = "jdbc:postgresql:demo",
user = "jeelona",
password = "jeelona"
)

DML
.insertInto(c)
.values(Country("code", "name", 1L).param)
Expand Down Expand Up @@ -96,29 +117,23 @@ object Main {

DML
.update(c)
.set(c.name := DQL.select("some name".literal))
.where(c.name in ("a".literal, "b".param))
.set(c.name := DQL.select("some name".literal).enclose, c.population := c.population - 1L.literal)
.where( (c.code :: c.name) in ("a".literal :: "b".param))
.update
.transactionally
.unsafeToFuture(db)

DML
.deleteFrom(c)
.where(c.population <= 0L.param)

findCountry[ConnectionIO]("Egypt").transact(xa).unsafeToFuture()
}

def slick: Future[Option[Country]] = {
import jam.slick.implicits._
import jam.slick.jdbcProfile.api._

Class.forName("org.postgresql.Driver")

val db: Database = Database.forURL(
url = "jdbc:postgresql:demo",
user = "jeelona",
password = "jeelona"
)
c.population + 1L.literal
c.population - 1L.literal
c.population * 1L.literal
c.population / 1L.literal
//c.name - "".literal

findCountry2("Egypt".param).query.transactionally.map(_.headOption).unsafeToFuture(db)
c.population > 1L.literal

findCountry[DBIO]("Egypt").unsafeToFuture(db)

Expand Down

0 comments on commit 4716fa8

Please sign in to comment.