Skip to content

Commit

Permalink
fix #90 - updates using a table column
Browse files Browse the repository at this point in the history
  • Loading branch information
fwbrasil committed Jan 7, 2016
1 parent 73c27f1 commit 0681b21
Show file tree
Hide file tree
Showing 18 changed files with 137 additions and 52 deletions.
36 changes: 36 additions & 0 deletions README.md
Expand Up @@ -421,6 +421,18 @@ db.run(a)(List((999, "+1510488988")))
// INSERT INTO Contact (personId,phone) VALUES (?, ?)
```

Or column queries:

```scala
val a = quote {
(id: Int) =>
query[Person].insert(_.id -> id, _.age -> query[Person].map(p => p.age).max)
}

db.run(a)(List(999))
// INSERT INTO Person (id,age) VALUES (?, (SELECT MAX(p.age) FROM Person p))
```

**update**
```scala
val a = quote {
Expand All @@ -443,6 +455,30 @@ db.run(a)(List((999, 18)))
// UPDATE Person SET age = ? WHERE id = ?
```

Using columns as part of the update:

```scala
val a = quote {
(id: Int) =>
query[Person].filter(p => p.id == id).update(p => p.age -> (p.age + 1))
}

db.run(a)(List(999))
// UPDATE Person SET age = (age + 1) WHERE id = ?
```

Using column a query:

```scala
val a = quote {
(id: Int) =>
query[Person].filter(p => p.id == id).update(_.age -> query[Person].map(p => p.age).max)
}

db.run(a)(List(999))
// UPDATE Person SET age = (SELECT MAX(p.age) FROM Person p) WHERE id = ?
```

**delete**
```scala
val a = quote {
Expand Down
2 changes: 1 addition & 1 deletion quill-core/src/main/scala/io/getquill/ast/Ast.scala
Expand Up @@ -96,7 +96,7 @@ case class Delete(query: Ast) extends Action

case class AssignedAction(action: Ast, assignments: List[Assignment]) extends Action

case class Assignment(property: String, value: Ast)
case class Assignment(input: Ident, property: String, value: Ast)

//************************************************************

Expand Down
2 changes: 1 addition & 1 deletion quill-core/src/main/scala/io/getquill/ast/AstShow.scala
Expand Up @@ -172,7 +172,7 @@ object AstShow {
implicit val assignmentShow: Show[Assignment] = new Show[Assignment] {
def show(e: Assignment) =
e match {
case Assignment(property, value) => s"_.$property -> ${value.show}"
case Assignment(ident, property, value) => s"$ident => $ident.$property -> ${value.show}"
}
}

Expand Down
Expand Up @@ -131,9 +131,9 @@ trait StatefulTransformer[T] {

def apply(e: Assignment): (Assignment, StatefulTransformer[T]) =
e match {
case Assignment(a, b) =>
val (bt, btt) = apply(b)
(Assignment(a, bt), btt)
case Assignment(a, b, c) =>
val (ct, ctt) = apply(c)
(Assignment(a, b, ct), ctt)
}

def apply[U, R](list: List[U])(f: StatefulTransformer[T] => U => (R, StatefulTransformer[T])) =
Expand Down
Expand Up @@ -21,17 +21,17 @@ trait StatelessTransformer {

def apply(e: Query): Query =
e match {
case e: Entity => e
case Filter(a, b, c) => Filter(apply(a), b, apply(c))
case Map(a, b, c) => Map(apply(a), b, apply(c))
case FlatMap(a, b, c) => FlatMap(apply(a), b, apply(c))
case SortBy(a, b, c, d) => SortBy(apply(a), b, apply(c), d)
case GroupBy(a, b, c) => GroupBy(apply(a), b, apply(c))
case Aggregation(o, a) => Aggregation(o, apply(a))
case Take(a, b) => Take(apply(a), apply(b))
case Drop(a, b) => Drop(apply(a), apply(b))
case Union(a, b) => Union(apply(a), apply(b))
case UnionAll(a, b) => UnionAll(apply(a), apply(b))
case e: Entity => e
case Filter(a, b, c) => Filter(apply(a), b, apply(c))
case Map(a, b, c) => Map(apply(a), b, apply(c))
case FlatMap(a, b, c) => FlatMap(apply(a), b, apply(c))
case SortBy(a, b, c, d) => SortBy(apply(a), b, apply(c), d)
case GroupBy(a, b, c) => GroupBy(apply(a), b, apply(c))
case Aggregation(o, a) => Aggregation(o, apply(a))
case Take(a, b) => Take(apply(a), apply(b))
case Drop(a, b) => Drop(apply(a), apply(b))
case Union(a, b) => Union(apply(a), apply(b))
case UnionAll(a, b) => UnionAll(apply(a), apply(b))
case OuterJoin(t, a, b, iA, iB, on) =>
OuterJoin(t, apply(a), apply(b), iA, iB, apply(on))
}
Expand Down Expand Up @@ -60,6 +60,6 @@ trait StatelessTransformer {

private def apply(e: Assignment): Assignment =
e match {
case Assignment(property, value) => Assignment(property, apply(value))
case Assignment(input, property, value) => Assignment(input, property, apply(value))
}
}
Expand Up @@ -36,6 +36,13 @@ case class FreeVariables(state: State)
super.apply(query)
}

override def apply(e: Assignment): (Assignment, StatefulTransformer[State]) =
e match {
case Assignment(a, b, c) =>
val (ct, ctt) = FreeVariables(State(state.seen + a, state.free))(c)
(Assignment(a, b, ct), ctt)
}

private def free(a: Ast, ident: Ident, c: Ast) = {
val (_, ta) = apply(a)
val (_, tc) = FreeVariables(State(state.seen + ident, state.free))(c)
Expand Down
Expand Up @@ -110,7 +110,7 @@ trait Liftables {
}

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

implicit val valueLiftable: Liftable[Value] = Liftable[Value] {
Expand Down
4 changes: 2 additions & 2 deletions quill-core/src/main/scala/io/getquill/quotation/Parsing.scala
Expand Up @@ -328,8 +328,8 @@ trait Parsing {
}

private val assignmentParser: Parser[Assignment] = Parser[Assignment] {
case q"(($x1) => scala.this.Predef.ArrowAssoc[$t]($x2.$prop).->[$v]($value))" =>
Assignment(prop.decodedName.toString, astParser(value))
case q"((${ identParser(i1) }) => scala.this.Predef.ArrowAssoc[$t](${ identParser(i2) }.$prop).->[$v]($value))" if (i1 == i2) =>
Assignment(i1, prop.decodedName.toString, astParser(value))
}

}
Expand Up @@ -120,7 +120,7 @@ trait Unliftables {
}

implicit val assignmentUnliftable: Unliftable[Assignment] = Unliftable[Assignment] {
case q"$pack.Assignment.apply(${ a: String }, ${ b: Ast })" => Assignment(a, b)
case q"$pack.Assignment.apply(${ a: Ident }, ${ b: String }, ${ c: Ast })" => Assignment(a, b, c)
}

implicit val valueUnliftable: Unliftable[Value] = Unliftable[Value] {
Expand Down
Expand Up @@ -21,7 +21,7 @@ trait ActionMacro extends EncodingMacro {
val encodingValue = encoding(param, Encoding.inferEncoder[S](c))(c.WeakTypeTag(tpe))
val bindings = bindingMap(encodingValue)
val idents = bindings.keys.toList
val assignedAction = AssignedAction(action, idents.map(k => Assignment(k.name, k)))
val assignedAction = AssignedAction(action, idents.map(k => Assignment(Ident("x"), k.name, k)))
val encodedParams = EncodeParams.raw[S](c)(bindings)
expandedTree(assignedAction, idents.toList, List(tpe), encodedParams)

Expand Down
8 changes: 4 additions & 4 deletions quill-core/src/test/scala/io/getquill/ast/AstShowSpec.scala
Expand Up @@ -391,10 +391,10 @@ class AstShowSpec extends Spec {
"update" - {
"assigned" in {
val q = quote {
query[TestEntity].filter(t => t.s == "test").update(_.s -> "a")
query[TestEntity].filter(t => t.s == "test").update(t => t.s -> "a")
}
(q.ast: Ast).show mustEqual
"""query[TestEntity].filter(t => t.s == "test").update(_.s -> "a")"""
"""query[TestEntity].filter(t => t.s == "test").update(t => t.s -> "a")"""
}
"unassigned" in {
val q = quote {
Expand All @@ -407,10 +407,10 @@ class AstShowSpec extends Spec {
"insert" - {
"assigned" in {
val q = quote {
query[TestEntity].insert(_.s -> "a")
query[TestEntity].insert(t => t.s -> "a")
}
(q.ast: Ast).show mustEqual
"""query[TestEntity].insert(_.s -> "a")"""
"""query[TestEntity].insert(t => t.s -> "a")"""
}
"unassigned" in {
val q = quote {
Expand Down
Expand Up @@ -170,11 +170,11 @@ class StatefulTransformerSpec extends Spec {
"action" - {
"update" - {
"assigned" in {
val ast: Ast = AssignedAction(Update(Ident("a")), List(Assignment("b", Ident("c"))))
Subject(Nil, Ident("a") -> Ident("a'"), Ident("b") -> Ident("b'"), Ident("c") -> Ident("c'"))(ast) match {
val ast: Ast = AssignedAction(Update(Ident("a")), List(Assignment(Ident("b"), "c", Ident("d"))))
Subject(Nil, Ident("a") -> Ident("a'"), Ident("b") -> Ident("b'"), Ident("c") -> Ident("c'"), Ident("d") -> Ident("d'"))(ast) match {
case (at, att) =>
at mustEqual AssignedAction(Update(Ident("a'")), List(Assignment("b", Ident("c'"))))
att.state mustEqual List(Ident("a"), Ident("c"))
at mustEqual AssignedAction(Update(Ident("a'")), List(Assignment(Ident("b"), "c", Ident("d'"))))
att.state mustEqual List(Ident("a"), Ident("d"))
}
}
"unassigned" in {
Expand All @@ -188,11 +188,11 @@ class StatefulTransformerSpec extends Spec {
}
"insert" - {
"assigned" in {
val ast: Ast = AssignedAction(Insert(Ident("a")), List(Assignment("b", Ident("c"))))
Subject(Nil, Ident("a") -> Ident("a'"), Ident("b") -> Ident("b'"), Ident("c") -> Ident("c'"))(ast) match {
val ast: Ast = AssignedAction(Insert(Ident("a")), List(Assignment(Ident("b"), "c", Ident("d"))))
Subject(Nil, Ident("a") -> Ident("a'"), Ident("b") -> Ident("b'"), Ident("c") -> Ident("c'"), Ident("d") -> Ident("d'"))(ast) match {
case (at, att) =>
at mustEqual AssignedAction(Insert(Ident("a'")), List(Assignment("b", Ident("c'"))))
att.state mustEqual List(Ident("a"), Ident("c"))
at mustEqual AssignedAction(Insert(Ident("a'")), List(Assignment(Ident("b"), "c", Ident("d'"))))
att.state mustEqual List(Ident("a"), Ident("d"))
}
}
"unassigned" in {
Expand Down
Expand Up @@ -111,9 +111,9 @@ class StatelessTransformerSpec extends Spec {
"action" - {
"update" - {
"assigned" in {
val ast: Ast = AssignedAction(Update(Ident("a")), List(Assignment("b", Ident("c"))))
Subject(Ident("a") -> Ident("a'"), Ident("b") -> Ident("b'"), Ident("c") -> Ident("c'"))(ast) mustEqual
AssignedAction(Update(Ident("a'")), List(Assignment("b", Ident("c'"))))
val ast: Ast = AssignedAction(Update(Ident("a")), List(Assignment(Ident("b"), "c", Ident("d"))))
Subject(Ident("a") -> Ident("a'"), Ident("b") -> Ident("b'"), Ident("c") -> Ident("c'"), Ident("d") -> Ident("d'"))(ast) mustEqual
AssignedAction(Update(Ident("a'")), List(Assignment(Ident("b"), "c", Ident("d'"))))
}
"unassigned" in {
val ast: Ast = Update(Ident("a"))
Expand All @@ -123,9 +123,9 @@ class StatelessTransformerSpec extends Spec {
}
"insert" - {
"assigned" in {
val ast: Ast = AssignedAction(Insert(Ident("a")), List(Assignment("b", Ident("c"))))
Subject(Ident("a") -> Ident("a'"), Ident("b") -> Ident("b'"), Ident("c") -> Ident("c'"))(ast) mustEqual
AssignedAction(Insert(Ident("a'")), List(Assignment("b", Ident("c'"))))
val ast: Ast = AssignedAction(Insert(Ident("a")), List(Assignment(Ident("b"), "c", Ident("d"))))
Subject(Ident("a") -> Ident("a'"), Ident("b") -> Ident("b'"), Ident("c") -> Ident("c'"), Ident("d") -> Ident("d'"))(ast) mustEqual
AssignedAction(Insert(Ident("a'")), List(Assignment(Ident("b"), "c", Ident("d'"))))
}
"unassigned" in {
val ast: Ast = Insert(Ident("a"))
Expand Down
Expand Up @@ -75,6 +75,13 @@ class FreeVariablesSpec extends Spec {
}
FreeVariables(q.ast) mustEqual Set(Ident("s"))
}
"assignment" in {
val s = 10
val q = quote {
qr1.insert(_.i -> s)
}
FreeVariables(q.ast) mustEqual Set(Ident("s"))
}
}

"takes in consideration variables defined in the quotation" - {
Expand Down Expand Up @@ -114,5 +121,11 @@ class FreeVariablesSpec extends Spec {
}
FreeVariables(q.ast) mustBe empty
}
"assignment" in {
val q = quote {
qr1.insert(t => t.i -> (t.i + 1))
}
FreeVariables(q.ast) mustBe empty
}
}
}
Expand Up @@ -231,9 +231,15 @@ class QuotationSpec extends Spec {
"update" - {
"assigned" in {
val q = quote {
qr1.update(_.s -> "s")
qr1.update(t => t.s -> "s")
}
quote(unquote(q)).ast mustEqual AssignedAction(Update(Entity("TestEntity")), List(Assignment("s", Constant("s"))))
quote(unquote(q)).ast mustEqual AssignedAction(Update(Entity("TestEntity")), List(Assignment(Ident("t"), "s", Constant("s"))))
}
"assigned using entity property" in {
val q = quote {
qr1.update(t => t.i -> (t.i + 1))
}
quote(unquote(q)).ast mustEqual AssignedAction(Update(Entity("TestEntity")), List(Assignment(Ident("t"), "i", BinaryOperation(Property(Ident("t"), "i"), NumericOperator.`+`, Constant(1)))))
}
"unassigned" in {
val q = quote {
Expand All @@ -245,9 +251,9 @@ class QuotationSpec extends Spec {
"insert" - {
"assigned" in {
val q = quote {
qr1.insert(_.s -> "s")
qr1.insert(t => t.s -> "s")
}
quote(unquote(q)).ast mustEqual AssignedAction(Insert(Entity("TestEntity")), List(Assignment("s", Constant("s"))))
quote(unquote(q)).ast mustEqual AssignedAction(Insert(Entity("TestEntity")), List(Assignment(Ident("t"), "s", Constant("s"))))
}
"unassigned" in {
val q = quote {
Expand Down
Expand Up @@ -38,7 +38,7 @@ class ActionMacroSpec extends Spec {
val r = mirrorSource.run(q)(
List(TestEntity("s", 1, 2L, Some(4)),
TestEntity("s2", 12, 22L, Some(42))))
r.ast.toString mustEqual "query[TestEntity].insert(_.s -> ?, _.i -> ?, _.l -> ?, _.o -> ?)"
r.ast.toString mustEqual "query[TestEntity].insert(x => x.s -> ?, x => x.i -> ?, x => x.l -> ?, x => x.o -> ?)"
r.bindList mustEqual List(
Row("s", 1, 2L, Some(4)),
Row("s2", 12, 22L, Some(42)))
Expand Down
Expand Up @@ -233,7 +233,7 @@ trait SqlIdiom {
implicit def actionShow(implicit strategy: NamingStrategy): Show[Action] = {

def set(assignments: List[Assignment]) =
assignments.map(a => s"${strategy.column(a.property)} = ${a.value.show}").mkString(", ")
assignments.map(a => s"${strategy.column(a.property)} = ${scopedShow(a.value)}").mkString(", ")

implicit def propertyShow: Show[Property] = new Show[Property] {
def show(e: Property) =
Expand All @@ -249,7 +249,7 @@ trait SqlIdiom {
case AssignedAction(Insert(table: Entity), assignments) =>
val columns = assignments.map(_.property).map(strategy.column(_))
val values = assignments.map(_.value)
s"INSERT INTO ${table.show} (${columns.mkString(",")}) VALUES (${values.show})"
s"INSERT INTO ${table.show} (${columns.mkString(",")}) VALUES (${values.map(scopedShow(_)).mkString(", ")})"

case AssignedAction(Update(table: Entity), assignments) =>
s"UPDATE ${table.show} SET ${set(assignments)}"
Expand Down
Expand Up @@ -503,12 +503,21 @@ class SqlIdiomSpec extends Spec {
}
}
"action" - {
"insert" in {
val q = quote {
qr1.insert(_.i -> 1, _.s -> "s")
"insert" - {
"simple" in {
val q = quote {
qr1.insert(_.i -> 1, _.s -> "s")
}
mirrorSource.run(q).sql mustEqual
"INSERT INTO TestEntity (i,s) VALUES (1, 's')"
}
"using nested select" in {
val q = quote {
qr1.insert(_.i -> qr2.map(t => t.i).max, _.s -> "s")
}
mirrorSource.run(q).sql mustEqual
"INSERT INTO TestEntity (i,s) VALUES ((SELECT MAX(t.i) FROM TestEntity2 t), 's')"
}
mirrorSource.run(q).sql mustEqual
"INSERT INTO TestEntity (i,s) VALUES (1, 's')"
}
"update" - {
"with filter" in {
Expand All @@ -525,6 +534,20 @@ class SqlIdiomSpec extends Spec {
mirrorSource.run(q).sql mustEqual
"UPDATE TestEntity SET s = 's'"
}
"using a table column" in {
val q = quote {
qr1.update(t => t.i -> (t.i + 1))
}
mirrorSource.run(q).sql mustEqual
"UPDATE TestEntity SET i = (i + 1)"
}
"using nested select" in {
val q = quote {
qr1.update(_.i -> qr2.map(t => t.i).max)
}
mirrorSource.run(q).sql mustEqual
"UPDATE TestEntity SET i = (SELECT MAX(t.i) FROM TestEntity2 t)"
}
}
"delete" - {
"with filter" in {
Expand Down

0 comments on commit 0681b21

Please sign in to comment.