Skip to content

Commit

Permalink
WIP upserts DSL, needs error checking, style fix some bugs, basically…
Browse files Browse the repository at this point in the history
… everything
  • Loading branch information
Stephen Carman committed Oct 19, 2016
1 parent 6f379c9 commit a00dc10
Show file tree
Hide file tree
Showing 13 changed files with 53 additions and 21 deletions.
1 change: 1 addition & 0 deletions quill-core/src/main/scala/io/getquill/MirrorIdiom.scala
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ class MirrorIdiom extends Idiom {
case Insert(query, assignments) => stmt"${query.token}.insert(${assignments.token})"
case Upsert(query, assignments) => stmt"${query.token}.upsert(${assignments.token})"
case Conflict(query, alias, body) => stmt"${query.token}.conflict((${alias.token}) => ${body.token})"
case ConflictUpdate(query, assignments) => stmt"${query.token}.conflictUpdate(${assignments.token})"
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}.forach((${alias.token}) => ${body.token})"
Expand Down
2 changes: 2 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 @@ -106,6 +106,8 @@ case class Insert(query: Ast, assignments: List[Assignment]) extends Action
case class Upsert(query: Ast, assignments: List[Assignment]) extends Action
case class Delete(query: Ast) extends Action
case class Conflict(query: Ast, alias: Ident, property: Ast) extends Action

case class ConflictUpdate(query: Ast, assignments: List[Assignment]) extends Action
case class Returning(action: Ast, alias: Ident, property: Ast) extends Action

case class Foreach(query: Ast, alias: Ident, body: Ast) extends Action
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ trait StatefulTransformer[T] {
val (at, att) = apply(a)
val (ct, ctt) = att.apply(c)
(Conflict(at, b, ct), ctt)
case ConflictUpdate(a, b) =>
val (at, att) = apply(a)
val (bt, btt) = att.apply(b)(_.apply)
(ConflictUpdate(at, bt), btt)
case Delete(a) =>
val (at, att) = apply(a)
(Delete(at), att)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ trait StatelessTransformer {
case Insert(query, assignments) => Insert(apply(query), assignments.map(apply))
case Upsert(query, assignments) => Upsert(apply(query), assignments.map(apply))
case Conflict(query, alias, property) => Conflict(apply(query), alias, apply(property))
case ConflictUpdate(query, assignments) => ConflictUpdate(apply(query), assignments.map(apply))
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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class ActionMacro(val c: MacroContext)
)
"""
}

def runActionReturning[T](quoted: Tree)(implicit t: WeakTypeTag[T]): Tree =
c.untypecheck {
q"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ trait Context[Idiom <: io.getquill.idiom.Idiom, Naming <: NamingStrategy]
def run[T](quoted: Quoted[Query[T]]): RunQueryResult[T] = macro QueryMacro.runQuery[T]
def run(quoted: Quoted[Action[_]]): RunActionResult = macro ActionMacro.runAction
def run[T](quoted: Quoted[ActionReturning[_, T]]): RunActionReturningResult[T] = macro ActionMacro.runActionReturning[T]
def run[T](quoted: Quoted[ConflictAction[_, T]]): RunActionReturningResult[T] = macro ActionMacro.runActionConflict[T]
def run[T](quoted: Quoted[ActionConflict[_, T]]): RunActionReturningResult[T] = macro ActionMacro.runActionConflict[T]
def run(quoted: Quoted[BatchAction[Action[_]]]): RunBatchActionResult = macro ActionMacro.runBatchAction
def run[T](quoted: Quoted[BatchAction[ActionReturning[_, T]]]): RunBatchActionReturningResult[T] = macro ActionMacro.runBatchActionReturning[T]

Expand Down
18 changes: 8 additions & 10 deletions quill-core/src/main/scala/io/getquill/dsl/QueryDsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -87,23 +87,21 @@ private[dsl] trait QueryDsl {
}

sealed trait ActionReturning[E, Output] extends Action[E]
sealed trait ConflictAction[E, Output] extends Action[E]

sealed trait Update[E] extends Action[E]
sealed trait Delete[E] extends Action[E]

sealed trait Upsert[E] extends Action[E]{
sealed trait ActionConflict[E, Output] extends Action[E] {
@compileTimeOnly(NonQuotedException.message)
def update(value: E): Action[E] = NonQuotedException()
def conflictUpdate(value: E): Action[E] = NonQuotedException()

@compileTimeOnly(NonQuotedException.message)
def update(f: (E => (Any, Any)), f2: (E => (Any, Any))*): Action[E] = NonQuotedException()
def conflictUpdate(f: (E => (Any, Any)), f2: (E => (Any, Any))*): Action[E] = NonQuotedException()
}

@compileTimeOnly(NonQuotedException.message)
def conflict[R](f: E => R): ConflictAction[E, R] = NonQuotedException()
sealed trait Update[E] extends Action[E]
sealed trait Delete[E] extends Action[E]

sealed trait Upsert[E] extends Action[E]{
@compileTimeOnly(NonQuotedException.message)
def returning[R](f: E => R): ActionReturning[E, R] = NonQuotedException()
def conflict[R](f: E => R): ActionConflict[E, R] = NonQuotedException()
}

sealed trait BatchAction[+A <: Action[_]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ trait Liftables {
case Insert(a, b) => q"$pack.Insert($a, $b)"
case Upsert(a, b) => q"$pack.Upsert($a, $b)"
case Conflict(a, b, c) => q"$pack.Conflict($a, $b, $c)"
case ConflictUpdate(a, b) => q"$pack.ConflictUpdate($a, $b)"
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)"
Expand Down
3 changes: 3 additions & 0 deletions quill-core/src/main/scala/io/getquill/quotation/Parsing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,9 @@ trait Parsing {
case q"$query.$method(..$assignments)" if (method.decodedName.toString == "upsert") =>
Upsert(astParser(query), assignments.map(assignmentParser(_)))

case q"$action.$method(..$assignments)" if (method.decodedName.toString == "conflictUpdate") =>
ConflictUpdate(astParser(action), assignments.map(assignmentParser(_)))

case q"$query.delete" =>
Delete(astParser(query))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ trait Unliftables {
case q"$pack.Insert.apply(${ a: Ast }, ${ b: List[Assignment] })" => Insert(a, b)
case q"$pack.Upsert.apply(${ a: Ast }, ${ b: List[Assignment] })" => Upsert(a, b)
case q"$pack.Conflict.apply(${ a: Ast }, ${ b: Ident }, ${ c: Ast })" => Conflict(a, b, c)
case q"$pack.ConflictUpdate.apply(${ a: Ast }, ${ b: List[Assignment]})" => ConflictUpdate(a, b)
case q"$pack.Delete.apply(${ a: Ast })" => Delete(a)
case q"$pack.Returning.apply(${ a: Ast }, ${ b: Ident }, ${ c: Ast })" => Returning(a, b, c)
case q"$pack.Foreach.apply(${ a: Ast }, ${ b: Ident }, ${ c: Ast })" => Foreach(a, b, c)
Expand Down
14 changes: 13 additions & 1 deletion quill-sql/src/main/scala/io/getquill/PostgresDialect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,22 @@ trait PostgresDialect
override def prepareForProbing(string: String) =
s"PREPARE p${preparedStatementId.incrementAndGet.toString.token} AS $string"


/*
This doesn't work correctly...
INSERT INTO TestEntity (v.s,v.l,v.o) VALUES (?, ?, ?) ON CONFLICT(x1.i) DO UPDATE SET x2.i = ?, x3.l = ?, x4.s = ?
override implicit def actionTokenizer(implicit strategy: NamingStrategy): Tokenizer[Action] = {
Tokenizer[Action] {
case Upsert(table: Entity, assignments) => super.actionTokenizer.token(Upsert(table, assignments))
case Upsert(table: Entity, assignments) =>
val columns = assignments.map(_.property.token)
val values = assignments.map(_.value)
stmt"INSERT INTO ${table.token} (${columns.mkStmt(",")}) VALUES (${values.map(scopedTokenizer(_)).mkStmt(", ")})"
case Conflict(action, prop, value) => stmt"${action.token} ON CONFLICT(${value.token})"
case ConflictUpdate(action, assignments) => stmt"${action.token} DO UPDATE SET ${assignments.mkStmt()}"
case action => super.actionTokenizer.token(action)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ trait SqlIdiom extends Idiom {
val values = assignments.map(_.value)
stmt"INSERT INTO ${table.token} (${columns.mkStmt(",")}) VALUES (${values.map(scopedTokenizer(_)).mkStmt(", ")})"

case Upsert(table: Entity, assignments) => actionTokenizer.token(Insert(table, assignments))
//case Upsert(table: Entity, assignments) => fail(s"Your dialect does not support upserts. :-(")

case Update(table: Entity, assignments) =>
stmt"UPDATE ${table.token} SET ${assignments.token}"
Expand All @@ -294,14 +294,25 @@ trait SqlIdiom extends Idiom {
case Delete(table: Entity) =>
stmt"DELETE FROM ${table.token}"

case Conflict(action, prop, value) =>
stmt"${action.token} ON CONFLICT(${value.token}) UPDATE ${prop.name.token}"
//case Conflict(action, prop, value) => fail(s"Your dialect does not support upserts. :-(")

case Returning(action, prop, value) =>
action.token

//case ConflictUpdate(action, assignments) => fail(s"Your dialect does not support upserts. :-(")
/*
When implemented here, the query is correct, why?
INSERT INTO TestEntity (s,l,o) VALUES (?, ?, ?) ON CONFLICT(i) DO UPDATE SET i = ?, l = ?, s = ?
*/
case Upsert(table: Entity, assignments) =>
val columns = assignments.map(_.property.token)
val values = assignments.map(_.value)
stmt"INSERT INTO ${table.token} (${columns.mkStmt(",")}) VALUES (${values.map(scopedTokenizer(_)).mkStmt(", ")})"
case Conflict(action, prop, value) => stmt"${action.token} ON CONFLICT(${value.token})"
case ConflictUpdate(action, assignments) => stmt"${action.token} DO UPDATE SET ${assignments.mkStmt()}"

case other =>
fail(s"Action ast can't be translated to sql: '$other'")
fail(s"Action ast can't be translated to sql: '$other' ${other.getClass}")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,9 @@ class PostgresDialectSpec extends Spec {
"upsert" in {
val e = TestEntity("", 1, 1L, Some(1))
val q = quote {
qr1.upsert(lift(e)).conflict(_.i)
query[TestEntity].upsert(lift(e)).conflict(_.i).conflictUpdate(_.i -> lift(2), _.l -> lift(1L), _.s -> lift("Test String"))
}
val i = quote {
qr1.insert(lift(TestEntity("", 1, 1L, Some(1))))
}
println(context.run(i).string)

println(context.run(q).string)
}
}
Expand Down

0 comments on commit a00dc10

Please sign in to comment.