Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
mentegy committed Feb 1, 2018
1 parent 0e87ed7 commit ce117c3
Show file tree
Hide file tree
Showing 24 changed files with 487 additions and 102 deletions.
6 changes: 6 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ lazy val `quill-sql` =
lazy val `quill-sql-jvm` = `quill-sql`.jvm
lazy val `quill-sql-js` = `quill-sql`.js

lazy val `quill-test` =
(project in file("quill-test"))
.settings(commonSettings: _*)
.settings(mimaSettings: _*)
.dependsOn(`quill-sql-jvm` % "compile->compile;test->test")

lazy val `quill-jdbc` =
(project in file("quill-jdbc"))
.settings(commonSettings: _*)
Expand Down
25 changes: 1 addition & 24 deletions build/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,27 +38,4 @@ done
echo -e "\nPostgres ready"

psql -h postgres -U postgres -c "CREATE DATABASE quill_test"
psql -h postgres -U postgres -d quill_test -a -f quill-sql/src/test/sql/postgres-schema.sql

echo "Waiting for Cassandra"
until nc -z cassandra 9042
do
printf "."
sleep 1
done
echo -e "\nCassandra ready"

echo "CREATE KEYSPACE quill_test WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};" > /tmp/create-keyspace.cql
cqlsh cassandra -f /tmp/create-keyspace.cql
cqlsh cassandra -k quill_test -f quill-cassandra/src/test/cql/cassandra-schema.cql

echo "Waiting for Sql Server"
until sqlcmd -S sqlserver -U SA -P 'QuillRocks!' -Q "SELECT 1" &> /dev/null
do
printf "."
sleep 1
done
echo -e "\nSql Server ready"

sqlcmd -S sqlserver -U SA -P "QuillRocks!" -Q "CREATE DATABASE quill_test"
sqlcmd -S sqlserver -U SA -P "QuillRocks!" -d quill_test -i quill-sql/src/test/sql/sqlserver-schema.sql
psql -h postgres -U postgres -d quill_test -a -f quill-sql/src/test/sql/postgres-schema.sql
12 changes: 6 additions & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ services:
links:
- postgres:postgres
- mysql:mysql
- cassandra:cassandra
- orientdb:orientdb
- sqlserver:sqlserver
#- cassandra:cassandra
#- orientdb:orientdb
#- sqlserver:sqlserver
volumes:
- ./:/app
command:
Expand All @@ -73,9 +73,9 @@ services:
links:
- postgres:postgres
- mysql:mysql
- cassandra:cassandra
- orientdb:orientdb
- sqlserver:sqlserver
#- cassandra:cassandra
#- orientdb:orientdb
#- sqlserver:sqlserver
volumes:
- ./:/app
- ~/.ivy2:/root/.ivy2
Expand Down
15 changes: 15 additions & 0 deletions quill-core/src/main/scala/io/getquill/MirrorIdiom.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class MirrorIdiom extends Idiom {
case ast: QuotedReference => ast.ast.token
case ast: Lift => ast.token
case ast: Assignment => ast.token
case Excluded => stmt"excluded"
case Existing => stmt"existing"
}

implicit def ifTokenizer(implicit liftTokenizer: Tokenizer[Lift]): Tokenizer[If] = Tokenizer[If] {
Expand Down Expand Up @@ -184,6 +186,19 @@ class MirrorIdiom extends Idiom {
case Delete(query) => stmt"${query.token}.delete"
case Returning(query, alias, body) => stmt"${query.token}.returning((${alias.token}) => ${body.token})"
case Foreach(query, alias, body) => stmt"${query.token}.foreach((${alias.token}) => ${body.token})"
case Conflict(query, t, a) => stmt"${query.token}.${tokenizeConflict(t, a)}"
}

private def tokenizeConflict(t: Conflict.Target, a: Conflict.Action): Statement = {
import Conflict._
(t, a) match {
case (NoTarget, Ignore) => stmt"onConflictIgnore"
case (StringValue(v), Ignore) => stmt"onConflictIgnore(${v.token})"
case (Properties(props), Ignore) => stmt"onConflictIgnore(${props.token})"
case (NoTarget, Update(assigns)) => stmt"onConflictUpdate(${assigns.token})"
case (StringValue(v), Update(assigns)) => stmt"onConflictUpdate(${v.token})(${assigns.token})"
case (Properties(props), Update(assigns)) => stmt"onConflictUpdate(${props.token})(${assigns.token})"
}
}

implicit def assignmentTokenizer(implicit liftTokenizer: Tokenizer[Lift]): Tokenizer[Assignment] = Tokenizer[Assignment] {
Expand Down
13 changes: 13 additions & 0 deletions quill-core/src/main/scala/io/getquill/ast/Ast.scala
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ case class If(condition: Ast, `then`: Ast, `else`: Ast) extends Ast

case class Assignment(alias: Ident, property: Ast, value: Ast) extends Ast

case object Excluded extends Ast
case object Existing extends Ast
//************************************************************

sealed trait Operation extends Ast
Expand Down Expand Up @@ -126,6 +128,17 @@ case class Returning(action: Ast, alias: Ident, property: Ast) extends Action

case class Foreach(query: Ast, alias: Ident, body: Ast) extends Action

case class Conflict(insert: Ast, target: Conflict.Target, action: Conflict.Action) extends Action
object Conflict {
trait Target
case object NoTarget extends Target
case class StringValue(value: String) extends Target
case class Properties(props: List[Property]) extends Target

trait Action
case object Ignore extends Action
case class Update(assignments: List[Assignment]) extends Action
}
//************************************************************

case class Dynamic(tree: Any) extends Ast
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ trait StatefulTransformer[T] {
case e: Ident => (e, this)
case e: OptionOperation => apply(e)
case e: TraversableOperation => apply(e)
case e: Property => apply(e)
case Existing | Excluded => (e, this)

case Function(a, b) =>
val (bt, btt) = apply(b)
(Function(a, bt), btt)

case Property(a, b) =>
val (at, att) = apply(a)
(Property(at, b), att)

case Infix(a, b) =>
val (bt, btt) = apply(b)(_.apply)
(Infix(a, bt), btt)
Expand Down Expand Up @@ -168,6 +166,13 @@ trait StatefulTransformer[T] {
(Assignment(a, bt, ct), ctt)
}

def apply(e: Property): (Property, StatefulTransformer[T]) =
e match {
case Property(a, b) =>
val (at, att) = apply(a)
(Property(at, b), att)
}

def apply(e: Operation): (Operation, StatefulTransformer[T]) =
e match {
case UnaryOperation(o, a) =>
Expand Down Expand Up @@ -217,6 +222,27 @@ trait StatefulTransformer[T] {
val (at, att) = apply(a)
val (ct, ctt) = att.apply(c)
(Foreach(at, b, ct), ctt)
case Conflict(a, b, c) =>
val (at, att) = apply(a)
val (bt, btt) = att.apply(b)
val (ct, ctt) = btt.apply(c)
(Conflict(at, bt, ct), ctt)
}

def apply(e: Conflict.Target): (Conflict.Target, StatefulTransformer[T]) =
e match {
case Conflict.NoTarget | Conflict.StringValue(_) => (e, this)
case Conflict.Properties(a) =>
val (at, att) = apply(a)(_.apply)
(Conflict.Properties(at), att)
}

def apply(e: Conflict.Action): (Conflict.Action, StatefulTransformer[T]) =
e match {
case Conflict.Ignore => (e, this)
case Conflict.Update(a) =>
val (at, att) = apply(a)(_.apply)
(Conflict.Update(at), att)
}

def apply[U, R](list: List[U])(f: StatefulTransformer[T] => U => (R, StatefulTransformer[T])) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ trait StatelessTransformer {
case e: Assignment => apply(e)
case Function(params, body) => Function(params, apply(body))
case e: Ident => e
case Property(a, name) => Property(apply(a), name)
case e: Property => apply(e)
case Infix(a, b) => Infix(a, b.map(apply))
case e: OptionOperation => apply(e)
case e: TraversableOperation => apply(e)
Expand All @@ -22,6 +22,7 @@ trait StatelessTransformer {
case Block(statements) => Block(statements.map(apply))
case Val(name, body) => Val(name, apply(body))
case o: Ordering => o
case Excluded | Existing => e
}

def apply(o: OptionOperation): OptionOperation =
Expand Down Expand Up @@ -69,6 +70,11 @@ trait StatelessTransformer {
case Assignment(a, b, c) => Assignment(a, apply(b), apply(c))
}

def apply(e: Property): Property =
e match {
case Property(a, name) => Property(apply(a), name)
}

def apply(e: Operation): Operation =
e match {
case UnaryOperation(o, a) => UnaryOperation(o, apply(a))
Expand All @@ -94,6 +100,19 @@ trait StatelessTransformer {
case Delete(query) => Delete(apply(query))
case Returning(query, alias, property) => Returning(apply(query), alias, apply(property))
case Foreach(query, alias, body) => Foreach(apply(query), alias, apply(body))
case Conflict(query, target, action) => Conflict(apply(query), apply(target), apply(action))
}

def apply(e: Conflict.Target): Conflict.Target =
e match {
case Conflict.NoTarget | Conflict.StringValue(_) => e
case Conflict.Properties(props) => Conflict.Properties(props.map(apply))
}

def apply(e: Conflict.Action): Conflict.Action =
e match {
case Conflict.Ignore => e
case Conflict.Update(assigns) => Conflict.Update(assigns.map(apply))
}

}
35 changes: 35 additions & 0 deletions quill-core/src/main/scala/io/getquill/dsl/QueryDsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,41 @@ private[dsl] trait QueryDsl {
sealed trait Insert[E] extends Action[E] {
@compileTimeOnly(NonQuotedException.message)
def returning[R](f: E => R): ActionReturning[E, R] = NonQuotedException()

@compileTimeOnly(NonQuotedException.message)
def onConflictIgnore: Insert[E] = NonQuotedException()

@compileTimeOnly(NonQuotedException.message)
def onConflictIgnore(target: String): Insert[E] = NonQuotedException()

@compileTimeOnly(NonQuotedException.message)
def onConflictIgnore(target: E => Any, targets: (E => Any)*): Insert[E] = NonQuotedException()

@compileTimeOnly(NonQuotedException.message)
def onConflictUpdate(assign: ((E, E) => (Any, Any)), assigns: ((E, E) => (Any, Any))*): Insert[E] = NonQuotedException()

@compileTimeOnly(NonQuotedException.message)
def onConflictUpdate(target: String)(assign: ((E, E) => (Any, Any)), assigns: ((E, E) => (Any, Any))*): Insert[E] = NonQuotedException()

/**
* Generates an atomic INSERT or UPDATE (upsert) action if supported.
*
* @param targets - conflict target
* @param assigns - update statement, declared as function: (table, excluded) => (assign, result).
* `table` - is used to extract column for update assignment and reference existing row
* `excluded` - aliases excluded table, e.g. row proposed for insertion.
* `assign` - left hand side of assignment. Should be accessed from `table` argument
* `result` - right hand side of assignment.
*
* Example usage:
* ```
* insert.onConflictUpdate(_.id)((t, e) => t.col -> (e.col + t.col))
* ```
* If insert statement violates conflict target then the column `col` of row will be updated with sum of
* existing value and and proposed `col` in insert.
*/
@compileTimeOnly(NonQuotedException.message)
def onConflictUpdate(target: E => Any, targets: (E => Any)*)(assign: ((E, E) => (Any, Any)), assigns: ((E, E) => (Any, Any))*): Insert[E] = NonQuotedException()
}

sealed trait ActionReturning[E, Output] extends Action[E]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ object NormalizeReturning {

def apply(e: Action): Action = {
e match {
case Returning(Insert(query, assignments), alias, body) =>
Returning(Insert(query, filterReturnedColumn(assignments, body)), alias, body)
case Returning(Update(query, assignments), alias, body) =>
Returning(Update(query, filterReturnedColumn(assignments, body)), alias, body)
case e => e
case Returning(a: Action, alias, body) => Returning(apply(a, body), alias, body)
case _ => e
}
}

private def apply(e: Action, body: Ast): Action = e match {
case Insert(query, assignments) => Insert(query, filterReturnedColumn(assignments, body))
case Update(query, assignments) => Update(query, filterReturnedColumn(assignments, body))
case Conflict(a: Action, target, act) => Conflict(apply(a, body), target, act)
case _ => e
}

private def filterReturnedColumn(assignments: List[Assignment], column: Ast): List[Assignment] =
assignments.flatMap(filterReturnedColumn(_, column))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ object RenameProperties extends StatelessTransformer {
val bodyr = BetaReduction(body, replace: _*)
(Returning(action, alias, bodyr), schema)
}
case Conflict(a: Action, target, act) =>
applySchema(a) match {
case (action, schema) => (Conflict(action, target, act), schema)
}
case q => (q, Tuple(List.empty))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ case class FreeVariables(state: State)
super.apply(other)
}

override def apply(e: Conflict.Target): (Conflict.Target, StatefulTransformer[State]) = (e, this)

override def apply(query: Query): (Query, StatefulTransformer[State]) =
query match {
case q @ Filter(a, b, c) => (q, free(a, b, c))
Expand Down
20 changes: 19 additions & 1 deletion quill-core/src/main/scala/io/getquill/quotation/Liftables.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ trait Liftables {
case ast: Assignment => assignmentLiftable(ast)
case ast: OptionOperation => optionOperationLiftable(ast)
case ast: TraversableOperation => traversableOperationLiftable(ast)
case ast: Property => propertyLiftable(ast)
case Val(name, body) => q"$pack.Val($name, $body)"
case Block(statements) => q"$pack.Block($statements)"
case Property(a, b) => q"$pack.Property($a, $b)"
case Function(a, b) => q"$pack.Function($a, $b)"
case FunctionApply(a, b) => q"$pack.FunctionApply($a, $b)"
case BinaryOperation(a, b, c) => q"$pack.BinaryOperation($a, $b, $c)"
Expand All @@ -33,6 +33,8 @@ trait Liftables {
case Dynamic(tree: Tree) if (tree.tpe <:< c.weakTypeOf[CoreDsl#Quoted[Any]]) => q"$tree.ast"
case Dynamic(tree: Tree) => q"$pack.Constant($tree)"
case QuotedReference(tree: Tree, ast) => q"$ast"
case Excluded => q"$pack.Excluded"
case Existing => q"$pack.Existing"
}

implicit val optionOperationLiftable: Liftable[OptionOperation] = Liftable[OptionOperation] {
Expand Down Expand Up @@ -113,6 +115,10 @@ trait Liftables {
case PropertyAlias(a, b) => q"$pack.PropertyAlias($a, $b)"
}

implicit val propertyLiftable: Liftable[Property] = Liftable[Property] {
case Property(a, b) => q"$pack.Property($a, $b)"
}

implicit val orderingLiftable: Liftable[Ordering] = Liftable[Ordering] {
case TupleOrdering(elems) => q"$pack.TupleOrdering($elems)"
case Asc => q"$pack.Asc"
Expand All @@ -136,6 +142,18 @@ trait Liftables {
case Delete(a) => q"$pack.Delete($a)"
case Returning(a, b, c) => q"$pack.Returning($a, $b, $c)"
case Foreach(a, b, c) => q"$pack.Foreach($a, $b, $c)"
case Conflict(a, b, c) => q"$pack.Conflict($a, $b, $c)"
}

implicit val conflictTargetLiftable: Liftable[Conflict.Target] = Liftable[Conflict.Target] {
case Conflict.NoTarget => q"$pack.Conflict.NoTarget"
case Conflict.StringValue(a) => q"$pack.Conflict.StringValue.apply($a)"
case Conflict.Properties(a) => q"$pack.Conflict.Properties.apply($a)"
}

implicit val conflictActionLiftable: Liftable[Conflict.Action] = Liftable[Conflict.Action] {
case Conflict.Ignore => q"$pack.Conflict.Ignore"
case Conflict.Update(a) => q"$pack.Conflict.Update.apply($a)"
}

implicit val assignmentLiftable: Liftable[Assignment] = Liftable[Assignment] {
Expand Down
Loading

0 comments on commit ce117c3

Please sign in to comment.