From 36c91f643ac22f2a8d5692360a9fce4ba78a4190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20L=C3=BCneberg?= Date: Wed, 30 Oct 2024 12:15:35 +0100 Subject: [PATCH 1/4] Introduce `updateSetOpt` fragment (#1893) --- .../main/scala/doobie/util/fragments.scala | 11 ++++++++ .../scala/doobie/util/FragmentsSuite.scala | 25 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/modules/core/src/main/scala/doobie/util/fragments.scala b/modules/core/src/main/scala/doobie/util/fragments.scala index 2b6083e4a..7261c432e 100644 --- a/modules/core/src/main/scala/doobie/util/fragments.scala +++ b/modules/core/src/main/scala/doobie/util/fragments.scala @@ -9,6 +9,7 @@ import cats.data.NonEmptyList import cats.syntax.all.* import cats.{Foldable, Functor, Reducible} import doobie.implicits.* +import doobie.Fragment.* /** Module of `Fragment` constructors. */ object fragments { @@ -17,6 +18,16 @@ object fragments { def values[F[_]: Reducible, A](fs: F[A])(implicit w: util.Write[A]): Fragment = fr"VALUES" ++ comma(fs.toNonEmptyList.map(f => parentheses(values(f)))) + /** Returns `UPDATE tableName SET columnUpdate0, columnUpdate1, ... WHERE (whereAnd0) AND (whereAnd1) ...`. */ + def updateSetOpt[F[_]: Foldable]( + tableName: Fragment, + columnUpdates: F[Fragment], + whereAnd: F[Fragment] + ): Option[Fragment] = { + NonEmptyList.fromFoldable(columnUpdates).map(cs => + fr"UPDATE" ++ tableName ++ set(cs) ++ fr"" ++ whereAndOpt(whereAnd)) + } + /** Returns `(f IN (fs0, fs1, ...))`. */ def in[A: util.Put](f: Fragment, fs0: A, fs1: A, fs: A*): Fragment = in(f, NonEmptyList(fs0, fs1 :: fs.toList)) diff --git a/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala b/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala index 68755325c..014f3b7e1 100644 --- a/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala +++ b/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala @@ -28,6 +28,7 @@ class FragmentsSuite extends munit.FunSuite { val someF: Option[Fragment] = Some(sql"${1}") val noneF: Option[Fragment] = None val ofs = List(Some(sql"${1}"), None, Some(sql"${3}")) + val sqlKv = nelInt.zipWith(NonEmptyList.of("a", "b", "c"))((v, k) => fr0"${Fragment.const0(k)} = $v").toList test("values for one column") { assertEquals(values(nelInt).query[Unit].sql, "VALUES (?) , (?) , (?) ") @@ -37,6 +38,30 @@ class FragmentsSuite extends munit.FunSuite { assertEquals(values(NonEmptyList.of((1, true), (2, false))).query[Unit].sql, "VALUES (?,?) , (?,?) ") } + test("updateSetOpt for three column") { + assertEquals( + updateSetOpt(Fragment.const("Foo"), sqlKv, List.empty[Fragment]).map(_.query[Unit].sql), + Some("UPDATE Foo SET a = ?, b = ?, c = ? ")) + } + + test("updateSetOpt for empty columns") { + assertEquals( + updateSetOpt(Fragment.const("Foo"), List.empty[Fragment], List.empty[Fragment]).map(_.query[Unit].sql), + None) + } + + test("updateSetOpt for three column and defined where clause") { + assertEquals( + updateSetOpt(Fragment.const("Foo"), sqlKv, List(fr0"id = 1")).map(_.query[Unit].sql), + Some("UPDATE Foo SET a = ?, b = ?, c = ? WHERE (id = 1)")) + } + + test("updateSetOpt for empty columns but defined where clause") { + assertEquals( + updateSetOpt(Fragment.const("Foo"), List.empty[Fragment], List(fr0"id = 1")).map(_.query[Unit].sql), + None) + } + test("in (1-column varargs)") { assertEquals(in(sql"foo", 1, 2, 3).query[Unit].sql, "(foo IN (? , ? , ? ) ) ") } From 6f8109b9f57c8be0b2218a652dab5b60b15a4e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20L=C3=BCneberg?= Date: Thu, 31 Oct 2024 22:26:39 +0100 Subject: [PATCH 2/4] Change api for condition --- .../core/src/main/scala/doobie/util/fragments.scala | 4 ++-- .../src/test/scala/doobie/util/FragmentsSuite.scala | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/core/src/main/scala/doobie/util/fragments.scala b/modules/core/src/main/scala/doobie/util/fragments.scala index 7261c432e..966c0b25f 100644 --- a/modules/core/src/main/scala/doobie/util/fragments.scala +++ b/modules/core/src/main/scala/doobie/util/fragments.scala @@ -22,10 +22,10 @@ object fragments { def updateSetOpt[F[_]: Foldable]( tableName: Fragment, columnUpdates: F[Fragment], - whereAnd: F[Fragment] + condition: Fragment ): Option[Fragment] = { NonEmptyList.fromFoldable(columnUpdates).map(cs => - fr"UPDATE" ++ tableName ++ set(cs) ++ fr"" ++ whereAndOpt(whereAnd)) + fr"UPDATE" ++ tableName ++ set(cs) ++ (if (condition == Fragment.empty) condition else fr"" ++ whereAnd(condition))) } /** Returns `(f IN (fs0, fs1, ...))`. */ diff --git a/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala b/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala index 014f3b7e1..d2032715f 100644 --- a/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala +++ b/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala @@ -40,25 +40,25 @@ class FragmentsSuite extends munit.FunSuite { test("updateSetOpt for three column") { assertEquals( - updateSetOpt(Fragment.const("Foo"), sqlKv, List.empty[Fragment]).map(_.query[Unit].sql), - Some("UPDATE Foo SET a = ?, b = ?, c = ? ")) + updateSetOpt(Fragment.const("Foo"), sqlKv, Fragment.empty).map(_.query[Unit].sql), + Some("UPDATE Foo SET a = ?, b = ?, c = ?")) } test("updateSetOpt for empty columns") { assertEquals( - updateSetOpt(Fragment.const("Foo"), List.empty[Fragment], List.empty[Fragment]).map(_.query[Unit].sql), + updateSetOpt(Fragment.const("Foo"), List.empty[Fragment], Fragment.empty).map(_.query[Unit].sql), None) } test("updateSetOpt for three column and defined where clause") { assertEquals( - updateSetOpt(Fragment.const("Foo"), sqlKv, List(fr0"id = 1")).map(_.query[Unit].sql), + updateSetOpt(Fragment.const("Foo"), sqlKv, fr0"id = 1").map(_.query[Unit].sql), Some("UPDATE Foo SET a = ?, b = ?, c = ? WHERE (id = 1)")) } test("updateSetOpt for empty columns but defined where clause") { assertEquals( - updateSetOpt(Fragment.const("Foo"), List.empty[Fragment], List(fr0"id = 1")).map(_.query[Unit].sql), + updateSetOpt(Fragment.const("Foo"), List.empty[Fragment], fr0"id = 1").map(_.query[Unit].sql), None) } From 63c053e80feb4557d164a2d0d6af6af4fb286347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20L=C3=BCneberg?= Date: Thu, 31 Oct 2024 22:39:08 +0100 Subject: [PATCH 3/4] Fix scalafmt --- modules/core/src/main/scala/doobie/util/fragments.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/core/src/main/scala/doobie/util/fragments.scala b/modules/core/src/main/scala/doobie/util/fragments.scala index 966c0b25f..c18611a56 100644 --- a/modules/core/src/main/scala/doobie/util/fragments.scala +++ b/modules/core/src/main/scala/doobie/util/fragments.scala @@ -25,7 +25,8 @@ object fragments { condition: Fragment ): Option[Fragment] = { NonEmptyList.fromFoldable(columnUpdates).map(cs => - fr"UPDATE" ++ tableName ++ set(cs) ++ (if (condition == Fragment.empty) condition else fr"" ++ whereAnd(condition))) + fr"UPDATE" ++ tableName ++ set(cs) ++ (if (condition == Fragment.empty) condition + else fr"" ++ whereAnd(condition))) } /** Returns `(f IN (fs0, fs1, ...))`. */ From 403e0213b728d8eb7463ebe2f1e6a1ac50ed20f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20L=C3=BCneberg?= Date: Sun, 3 Nov 2024 10:17:26 +0100 Subject: [PATCH 4/4] Remove where clause --- .../src/main/scala/doobie/util/fragments.scala | 9 +++------ .../scala/doobie/util/FragmentsSuite.scala | 18 ++---------------- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/modules/core/src/main/scala/doobie/util/fragments.scala b/modules/core/src/main/scala/doobie/util/fragments.scala index c18611a56..cb567094d 100644 --- a/modules/core/src/main/scala/doobie/util/fragments.scala +++ b/modules/core/src/main/scala/doobie/util/fragments.scala @@ -18,15 +18,12 @@ object fragments { def values[F[_]: Reducible, A](fs: F[A])(implicit w: util.Write[A]): Fragment = fr"VALUES" ++ comma(fs.toNonEmptyList.map(f => parentheses(values(f)))) - /** Returns `UPDATE tableName SET columnUpdate0, columnUpdate1, ... WHERE (whereAnd0) AND (whereAnd1) ...`. */ + /** Returns `UPDATE tableName SET columnUpdate0, columnUpdate1, ...`. */ def updateSetOpt[F[_]: Foldable]( tableName: Fragment, - columnUpdates: F[Fragment], - condition: Fragment + columnUpdates: F[Fragment] ): Option[Fragment] = { - NonEmptyList.fromFoldable(columnUpdates).map(cs => - fr"UPDATE" ++ tableName ++ set(cs) ++ (if (condition == Fragment.empty) condition - else fr"" ++ whereAnd(condition))) + NonEmptyList.fromFoldable(columnUpdates).map(cs => fr"UPDATE" ++ tableName ++ set(cs)) } /** Returns `(f IN (fs0, fs1, ...))`. */ diff --git a/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala b/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala index d2032715f..a642ac3af 100644 --- a/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala +++ b/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala @@ -40,26 +40,12 @@ class FragmentsSuite extends munit.FunSuite { test("updateSetOpt for three column") { assertEquals( - updateSetOpt(Fragment.const("Foo"), sqlKv, Fragment.empty).map(_.query[Unit].sql), + updateSetOpt(Fragment.const("Foo"), sqlKv).map(_.query[Unit].sql), Some("UPDATE Foo SET a = ?, b = ?, c = ?")) } test("updateSetOpt for empty columns") { - assertEquals( - updateSetOpt(Fragment.const("Foo"), List.empty[Fragment], Fragment.empty).map(_.query[Unit].sql), - None) - } - - test("updateSetOpt for three column and defined where clause") { - assertEquals( - updateSetOpt(Fragment.const("Foo"), sqlKv, fr0"id = 1").map(_.query[Unit].sql), - Some("UPDATE Foo SET a = ?, b = ?, c = ? WHERE (id = 1)")) - } - - test("updateSetOpt for empty columns but defined where clause") { - assertEquals( - updateSetOpt(Fragment.const("Foo"), List.empty[Fragment], fr0"id = 1").map(_.query[Unit].sql), - None) + assertEquals(updateSetOpt(Fragment.const("Foo"), List.empty[Fragment]).map(_.query[Unit].sql), None) } test("in (1-column varargs)") {