From abcfe6181d490dcee478334e42579f682111fc60 Mon Sep 17 00:00:00 2001 From: satorg Date: Sat, 2 Nov 2024 20:19:09 -0700 Subject: [PATCH 1/5] add fragment builders for `IN` and `NOT IN` operators --- .../main/scala/doobie/util/fragments.scala | 43 ++++++++++++++++- .../scala/doobie/util/FragmentsSuite.scala | 48 ++++++++++++++++++- 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/scala/doobie/util/fragments.scala b/modules/core/src/main/scala/doobie/util/fragments.scala index cb567094d..45cea7ed2 100644 --- a/modules/core/src/main/scala/doobie/util/fragments.scala +++ b/modules/core/src/main/scala/doobie/util/fragments.scala @@ -5,9 +5,11 @@ package doobie package util +import cats.Foldable +import cats.Functor +import cats.Reducible import cats.data.NonEmptyList import cats.syntax.all.* -import cats.{Foldable, Functor, Reducible} import doobie.implicits.* import doobie.Fragment.* @@ -54,6 +56,45 @@ object fragments { NonEmptyList.fromFoldable(fs).map(nel => notIn(f, nel)) } + @inline + private def inNotInValues[F[_]: Reducible: Functor, A]( + fwop: Fragment, + fs: F[A], + fallback: Fragment + )(implicit A: util.Write[A]): Fragment = { + if (A.length == 0) fallback + else { + fwop ++ { + if (A.length == 1) // no need for extra parentheses + parentheses(comma(fs.map(values(_)))) + else + parentheses(comma(fs.map(a => parentheses0(values(a))))) + } + } + } + + /** Returns `f IN (fs0, fs1, ...)`. + * @param f + * left-hand expression. + * @param fs + * values of `Product` type to compare to the left-hand expression. + * @return + * the `IN` subquery expression or `FALSE` if `fs` is a 0-arity product. + */ + def inValues[F[_]: Reducible: Functor, A: util.Write](f: Fragment, fs: F[A]): Fragment = + inNotInValues(f ++ fr" IN", fs, fr"FALSE") + + /** Returns `f NOT IN (fs0, fs1, ...)`. + * @param f + * left-hand expression. + * @param fs + * values of `Product` type to compare to the left-hand expression. + * @return + * the `NOT IN` subquery expression or `TRUE` if `fs` is a 0-arity product. + */ + def notInValues[F[_]: Reducible: Functor, A: util.Write](f: Fragment, fs: F[A]): Fragment = + inNotInValues(f ++ fr" NOT IN", fs, fr"TRUE") + /** Returns `(f1 AND f2 AND ... fn)`. */ def and(f1: Fragment, f2: Fragment, fs: Fragment*): Fragment = and(NonEmptyList(f1, f2 :: 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 a642ac3af..fedc30424 100644 --- a/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala +++ b/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala @@ -5,8 +5,9 @@ package doobie.util import cats.data.NonEmptyList -import doobie.*, doobie.implicits.* import cats.effect.IO +import doobie.* +import doobie.implicits.* class FragmentsSuite extends munit.FunSuite { import Fragments.* @@ -20,7 +21,10 @@ class FragmentsSuite extends munit.FunSuite { logHandler = None ) + val nelUnit100 = NonEmptyList.fromListUnsafe(List.fill(100)(())) val nelInt = NonEmptyList.of(1, 2, 3) + val nelIntBool2 = NonEmptyList.of((1, true), (2, false)) + val nelStrDblInt3 = NonEmptyList.of(("abc", 1.2, 3), ("def", 4.5, 6), ("ghi", 7.8, 9)) val listInt = nelInt.toList val nel1 = NonEmptyList.of(1).map(i => sql"$i") val nel = NonEmptyList.of(1, 2, 3).map(i => sql"$i") @@ -68,6 +72,48 @@ class FragmentsSuite extends munit.FunSuite { assertEquals(in(sql"foo", NonEmptyList.of((1, true), (2, false))).query[Unit].sql, "(foo IN ((?,?), (?,?)) ) ") } + test("inValues for no columns") { + assertEquals( + inValues(fr0"foo.bar", nelUnit100).query[Unit].sql, + "FALSE ") + } + test("inValues for one column") { + assertEquals( + inValues(fr0"foo.bar", nelInt).query[Unit].sql, + "foo.bar IN (?, ?, ?) ") + } + test("inValues for two columns") { + assertEquals( + inValues(fr0"foo.bar", nelIntBool2).query[Unit].sql, + "foo.bar IN ((?,?), (?,?)) ") + } + test("inValues for three columns") { + assertEquals( + inValues(fr0"foo.bar", nelStrDblInt3).query[Unit].sql, + "foo.bar IN ((?,?,?), (?,?,?), (?,?,?)) ") + } + + test("notInValues for no columns") { + assertEquals( + notInValues(fr0"foo.bar", nelUnit100).query[Unit].sql, + "TRUE ") + } + test("notInValues for one column") { + assertEquals( + notInValues(fr0"foo.bar", nelInt).query[Unit].sql, + "foo.bar NOT IN (?, ?, ?) ") + } + test("notInValues for two columns") { + assertEquals( + notInValues(fr0"foo.bar", nelIntBool2).query[Unit].sql, + "foo.bar NOT IN ((?,?), (?,?)) ") + } + test("notInValues for three columns") { + assertEquals( + notInValues(fr0"foo.bar", nelStrDblInt3).query[Unit].sql, + "foo.bar NOT IN ((?,?,?), (?,?,?), (?,?,?)) ") + } + test("notIn (varargs many)") { assertEquals(notIn(sql"foo", 1, 2, 3).query[Unit].sql, "(foo NOT IN (? , ? , ? ) ) ") } From 24eac91e1f93d5e63ae16454f092ce88a8031fe0 Mon Sep 17 00:00:00 2001 From: satorg Date: Tue, 5 Nov 2024 21:31:06 -0800 Subject: [PATCH 2/5] remove check for 0-arity `Read` --- .../main/scala/doobie/util/fragments.scala | 28 +++++++------------ .../scala/doobie/util/FragmentsSuite.scala | 11 -------- 2 files changed, 10 insertions(+), 29 deletions(-) diff --git a/modules/core/src/main/scala/doobie/util/fragments.scala b/modules/core/src/main/scala/doobie/util/fragments.scala index 45cea7ed2..c232614e8 100644 --- a/modules/core/src/main/scala/doobie/util/fragments.scala +++ b/modules/core/src/main/scala/doobie/util/fragments.scala @@ -10,8 +10,8 @@ import cats.Functor import cats.Reducible import cats.data.NonEmptyList import cats.syntax.all.* -import doobie.implicits.* import doobie.Fragment.* +import doobie.implicits.* /** Module of `Fragment` constructors. */ object fragments { @@ -57,21 +57,13 @@ object fragments { } @inline - private def inNotInValues[F[_]: Reducible: Functor, A]( - fwop: Fragment, - fs: F[A], - fallback: Fragment - )(implicit A: util.Write[A]): Fragment = { - if (A.length == 0) fallback - else { - fwop ++ { - if (A.length == 1) // no need for extra parentheses - parentheses(comma(fs.map(values(_)))) - else - parentheses(comma(fs.map(a => parentheses0(values(a))))) - } - } - } + private def constSubqueryExpr[F[_]: Reducible: Functor, A](fs: F[A])(implicit A: util.Write[A]): Fragment = + parentheses(comma { + if (A.length == 1) // no need for extra parentheses + fs.map(values(_)) + else + fs.map(a => parentheses0(values(a))) + }) /** Returns `f IN (fs0, fs1, ...)`. * @param f @@ -82,7 +74,7 @@ object fragments { * the `IN` subquery expression or `FALSE` if `fs` is a 0-arity product. */ def inValues[F[_]: Reducible: Functor, A: util.Write](f: Fragment, fs: F[A]): Fragment = - inNotInValues(f ++ fr" IN", fs, fr"FALSE") + f ++ fr" IN" ++ constSubqueryExpr(fs) /** Returns `f NOT IN (fs0, fs1, ...)`. * @param f @@ -93,7 +85,7 @@ object fragments { * the `NOT IN` subquery expression or `TRUE` if `fs` is a 0-arity product. */ def notInValues[F[_]: Reducible: Functor, A: util.Write](f: Fragment, fs: F[A]): Fragment = - inNotInValues(f ++ fr" NOT IN", fs, fr"TRUE") + f ++ fr" NOT IN" ++ constSubqueryExpr(fs) /** Returns `(f1 AND f2 AND ... fn)`. */ def and(f1: Fragment, f2: Fragment, fs: Fragment*): Fragment = diff --git a/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala b/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala index fedc30424..21465efac 100644 --- a/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala +++ b/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala @@ -21,7 +21,6 @@ class FragmentsSuite extends munit.FunSuite { logHandler = None ) - val nelUnit100 = NonEmptyList.fromListUnsafe(List.fill(100)(())) val nelInt = NonEmptyList.of(1, 2, 3) val nelIntBool2 = NonEmptyList.of((1, true), (2, false)) val nelStrDblInt3 = NonEmptyList.of(("abc", 1.2, 3), ("def", 4.5, 6), ("ghi", 7.8, 9)) @@ -72,11 +71,6 @@ class FragmentsSuite extends munit.FunSuite { assertEquals(in(sql"foo", NonEmptyList.of((1, true), (2, false))).query[Unit].sql, "(foo IN ((?,?), (?,?)) ) ") } - test("inValues for no columns") { - assertEquals( - inValues(fr0"foo.bar", nelUnit100).query[Unit].sql, - "FALSE ") - } test("inValues for one column") { assertEquals( inValues(fr0"foo.bar", nelInt).query[Unit].sql, @@ -93,11 +87,6 @@ class FragmentsSuite extends munit.FunSuite { "foo.bar IN ((?,?,?), (?,?,?), (?,?,?)) ") } - test("notInValues for no columns") { - assertEquals( - notInValues(fr0"foo.bar", nelUnit100).query[Unit].sql, - "TRUE ") - } test("notInValues for one column") { assertEquals( notInValues(fr0"foo.bar", nelInt).query[Unit].sql, From 199138b62a7cdb2c6427837684b317f5ddb71a9c Mon Sep 17 00:00:00 2001 From: satorg Date: Tue, 5 Nov 2024 23:50:37 -0800 Subject: [PATCH 3/5] optimize `constSubqueryExpr` --- .../core/src/main/scala/doobie/util/fragments.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/core/src/main/scala/doobie/util/fragments.scala b/modules/core/src/main/scala/doobie/util/fragments.scala index c232614e8..0c4250de7 100644 --- a/modules/core/src/main/scala/doobie/util/fragments.scala +++ b/modules/core/src/main/scala/doobie/util/fragments.scala @@ -57,13 +57,15 @@ object fragments { } @inline - private def constSubqueryExpr[F[_]: Reducible: Functor, A](fs: F[A])(implicit A: util.Write[A]): Fragment = - parentheses(comma { + private def constSubqueryExpr[F[_]: Reducible, A](fs: F[A])(implicit A: util.Write[A]): Fragment = { + val row: A => Fragment = if (A.length == 1) // no need for extra parentheses - fs.map(values(_)) + a => values(a) else - fs.map(a => parentheses0(values(a))) - }) + a => parentheses0(values(a)) + + parentheses(fs.reduceLeftTo(row) { _ ++ fr"," ++ row(_) }) + } /** Returns `f IN (fs0, fs1, ...)`. * @param f From a0723a91ef8c494fad12015dcc3afd1b48a45579 Mon Sep 17 00:00:00 2001 From: satorg Date: Wed, 6 Nov 2024 00:04:02 -0800 Subject: [PATCH 4/5] add `constSubqueryExprOpt` --- .../core/src/main/scala/doobie/util/fragments.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/modules/core/src/main/scala/doobie/util/fragments.scala b/modules/core/src/main/scala/doobie/util/fragments.scala index 0c4250de7..b294d9c86 100644 --- a/modules/core/src/main/scala/doobie/util/fragments.scala +++ b/modules/core/src/main/scala/doobie/util/fragments.scala @@ -67,6 +67,18 @@ object fragments { parentheses(fs.reduceLeftTo(row) { _ ++ fr"," ++ row(_) }) } + @inline + private def constSubqueryExprOpt[F[_]: Foldable, A](fs: F[A])(implicit A: util.Write[A]): Option[Fragment] = { + val row: A => Fragment = + if (A.length == 1) // no need for extra parentheses + a => values(a) + else + a => parentheses0(values(a)) + + fs.reduceLeftToOption(row) { _ ++ fr"," ++ row(_) } + .map(parentheses) + } + /** Returns `f IN (fs0, fs1, ...)`. * @param f * left-hand expression. From 313e9293d88d46142f98001d31f4822396c23ff6 Mon Sep 17 00:00:00 2001 From: satorg Date: Wed, 6 Nov 2024 00:39:42 -0800 Subject: [PATCH 5/5] replace `in[Put]` with `in[Write]` --- .../main/scala/doobie/util/fragments.scala | 90 +++++------ .../scala/doobie/util/FragmentsSuite.scala | 146 ++++++++++++------ 2 files changed, 144 insertions(+), 92 deletions(-) diff --git a/modules/core/src/main/scala/doobie/util/fragments.scala b/modules/core/src/main/scala/doobie/util/fragments.scala index b294d9c86..1f441c464 100644 --- a/modules/core/src/main/scala/doobie/util/fragments.scala +++ b/modules/core/src/main/scala/doobie/util/fragments.scala @@ -6,7 +6,6 @@ package doobie package util import cats.Foldable -import cats.Functor import cats.Reducible import cats.data.NonEmptyList import cats.syntax.all.* @@ -29,77 +28,80 @@ object fragments { } /** Returns `(f IN (fs0, fs1, ...))`. */ - def in[A: util.Put](f: Fragment, fs0: A, fs1: A, fs: A*): Fragment = + def in[A: util.Write](f: Fragment, fs0: A, fs1: A, fs: A*): Fragment = in(f, NonEmptyList(fs0, fs1 :: fs.toList)) - /** Returns `(f IN (fs0, fs1, ...))`. */ - def in[F[_]: Reducible: Functor, A: util.Put](f: Fragment, fs: F[A]): Fragment = - parentheses(f ++ fr" IN" ++ parentheses(comma(fs.map(a => fr"$a")))) - - def inOpt[F[_]: Foldable, A: util.Put](f: Fragment, fs: F[A]): Option[Fragment] = - NonEmptyList.fromFoldable(fs).map(nel => in(f, nel)) - - /** Returns `(f IN ((fs0-A, fs0-B), (fs1-A, fs1-B), ...))`. */ - def in[F[_]: Reducible: Functor, A: util.Put, B: util.Put](f: Fragment, fs: F[(A, B)]): Fragment = - parentheses(f ++ fr" IN" ++ parentheses(comma(fs.map { case (a, b) => fr0"($a,$b)" }))) - /** Returns `(f NOT IN (fs0, fs1, ...))`. */ - def notIn[A: util.Put](f: Fragment, fs0: A, fs1: A, fs: A*): Fragment = + def notIn[A: util.Write](f: Fragment, fs0: A, fs1: A, fs: A*): Fragment = notIn(f, NonEmptyList(fs0, fs1 :: fs.toList)) - /** Returns `(f NOT IN (fs0, fs1, ...))`. */ - def notIn[F[_]: Reducible: Functor, A: util.Put](f: Fragment, fs: F[A]): Fragment = { - parentheses(f ++ fr" NOT IN" ++ parentheses(comma(fs.map(a => fr"$a")))) - } - - def notInOpt[F[_]: Foldable, A: util.Put](f: Fragment, fs: F[A]): Option[Fragment] = { - NonEmptyList.fromFoldable(fs).map(nel => notIn(f, nel)) - } + @inline + private def mkRowFn[A](implicit A: util.Write[A]): A => Fragment = + if (A.length == 1) // no need for extra parentheses + a => values(a) + else + a => parentheses0(values(a)) @inline - private def constSubqueryExpr[F[_]: Reducible, A](fs: F[A])(implicit A: util.Write[A]): Fragment = { - val row: A => Fragment = - if (A.length == 1) // no need for extra parentheses - a => values(a) - else - a => parentheses0(values(a)) - - parentheses(fs.reduceLeftTo(row) { _ ++ fr"," ++ row(_) }) + private def constSubqueryExpr[F[_]: Reducible, A: util.Write](fs: F[A]): Fragment = { + val row = mkRowFn[A] + parentheses0(fs.reduceLeftTo(row) { _ ++ fr"," ++ row(_) }) } @inline - private def constSubqueryExprOpt[F[_]: Foldable, A](fs: F[A])(implicit A: util.Write[A]): Option[Fragment] = { - val row: A => Fragment = - if (A.length == 1) // no need for extra parentheses - a => values(a) - else - a => parentheses0(values(a)) - + private def constSubqueryExprOpt[F[_]: Foldable, A: util.Write](fs: F[A]): Option[Fragment] = { + val row = mkRowFn[A] fs.reduceLeftToOption(row) { _ ++ fr"," ++ row(_) } - .map(parentheses) + .map(parentheses0) } /** Returns `f IN (fs0, fs1, ...)`. + * + * @param f + * left-hand expression. + * @param fs + * values of `Product` type to compare to the left-hand expression. + * @return + * the result `IN` expression. + */ + def in[F[_]: Reducible, A: util.Write](f: Fragment, fs: F[A]): Fragment = + parentheses(f ++ fr" IN" ++ constSubqueryExpr(fs)) + + /** Returns `f IN (fs0, fs1, ...)`. + * * @param f * left-hand expression. * @param fs * values of `Product` type to compare to the left-hand expression. * @return - * the `IN` subquery expression or `FALSE` if `fs` is a 0-arity product. + * the result `IN` expression enclosed in `Some` or `None` if `fs` is empty. */ - def inValues[F[_]: Reducible: Functor, A: util.Write](f: Fragment, fs: F[A]): Fragment = - f ++ fr" IN" ++ constSubqueryExpr(fs) + def inOpt[F[_]: Foldable, A: util.Write](f: Fragment, fs: F[A]): Option[Fragment] = + constSubqueryExprOpt(fs).map(expr => parentheses(f ++ fr" IN" ++ expr)) /** Returns `f NOT IN (fs0, fs1, ...)`. + * + * @param f + * left-hand expression. + * @param fs + * values of `Product` type to compare to the left-hand expression. + * @return + * the result `NOT IN` subquery expression. + */ + def notIn[F[_]: Reducible, A: util.Write](f: Fragment, fs: F[A]): Fragment = + parentheses(f ++ fr" NOT IN" ++ constSubqueryExpr(fs)) + + /** Returns `f NOT IN (fs0, fs1, ...)`. + * * @param f * left-hand expression. * @param fs * values of `Product` type to compare to the left-hand expression. * @return - * the `NOT IN` subquery expression or `TRUE` if `fs` is a 0-arity product. + * the result `NOT IN` subquery expression enclosed in `Some` or `None` if `fs` is empty. */ - def notInValues[F[_]: Reducible: Functor, A: util.Write](f: Fragment, fs: F[A]): Fragment = - f ++ fr" NOT IN" ++ constSubqueryExpr(fs) + def notInOpt[F[_]: Foldable, A: util.Write](f: Fragment, fs: F[A]): Option[Fragment] = + constSubqueryExprOpt(fs).map(expr => parentheses(f ++ fr" NOT IN" ++ expr)) /** Returns `(f1 AND f2 AND ... fn)`. */ def and(f1: Fragment, f2: Fragment, fs: Fragment*): Fragment = diff --git a/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala b/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala index 21465efac..e3f2626b6 100644 --- a/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala +++ b/modules/core/src/test/scala/doobie/util/FragmentsSuite.scala @@ -24,7 +24,9 @@ class FragmentsSuite extends munit.FunSuite { val nelInt = NonEmptyList.of(1, 2, 3) val nelIntBool2 = NonEmptyList.of((1, true), (2, false)) val nelStrDblInt3 = NonEmptyList.of(("abc", 1.2, 3), ("def", 4.5, 6), ("ghi", 7.8, 9)) - val listInt = nelInt.toList + val listInt3 = nelInt.toList + val listIntBool2 = nelIntBool2.toList + val listStrDblInt3 = nelStrDblInt3.toList val nel1 = NonEmptyList.of(1).map(i => sql"$i") val nel = NonEmptyList.of(1, 2, 3).map(i => sql"$i") val fs = nel.toList @@ -51,80 +53,128 @@ class FragmentsSuite extends munit.FunSuite { assertEquals(updateSetOpt(Fragment.const("Foo"), List.empty[Fragment]).map(_.query[Unit].sql), None) } - test("in (1-column varargs)") { - assertEquals(in(sql"foo", 1, 2, 3).query[Unit].sql, "(foo IN (? , ? , ? ) ) ") + test("in (1-column, varargs)") { + assertEquals( + in(sql"foo.bar", 1, 2).query[Unit].sql, + "(foo.bar IN (?, ?)) ") + assertEquals( + in(sql"foo.bar", 3, 4, 5).query[Unit].sql, + "(foo.bar IN (?, ?, ?)) ") } - - test("in (1-column Reducible many)") { - assertEquals(in(sql"foo", nelInt).query[Unit].sql, "(foo IN (? , ? , ? ) ) ") + test("in (2-column, varargs)") { + assertEquals( + in(sql"foo.bar", 1 -> "one", 2 -> "two").query[Unit].sql, + "(foo.bar IN ((?,?), (?,?))) ") + assertEquals( + in(sql"foo.bar", 3 -> "three", 4 -> "four", 5 -> "five").query[Unit].sql, + "(foo.bar IN ((?,?), (?,?), (?,?))) ") } - - test("inOpt (1-column Reducible empty)") { - assertEquals(inOpt(sql"foo", List.empty[Int]).map(_.query[Unit].sql), None) + test("in (3-column, varargs)") { + assertEquals( + in(sql"foo.bar", (1.2, "A", 3), (4.5, "B", 6)).query[Unit].sql, + "(foo.bar IN ((?,?,?), (?,?,?))) ") + assertEquals( + in(sql"foo.bar", (9.8, "Z", 7), (6.5, "Y", 4), (3.2, "X", 1)).query[Unit].sql, + "(foo.bar IN ((?,?,?), (?,?,?), (?,?,?))) ") } - test("inOpt (1-column Reducible many)") { - assertEquals(inOpt(sql"foo", listInt).map(_.query[Unit].sql), Some("(foo IN (? , ? , ? ) ) ")) + test("notIn (1-column, varargs)") { + assertEquals( + notIn(sql"foo.bar", 1, 2).query[Unit].sql, + "(foo.bar NOT IN (?, ?)) ") + assertEquals( + notIn(sql"foo.bar", 3, 4, 5).query[Unit].sql, + "(foo.bar NOT IN (?, ?, ?)) ") } - - test("in (2-column varargs)") { - assertEquals(in(sql"foo", NonEmptyList.of((1, true), (2, false))).query[Unit].sql, "(foo IN ((?,?), (?,?)) ) ") + test("notIn (2-column, varargs)") { + assertEquals( + notIn(sql"foo.bar", 1 -> "one", 2 -> "two").query[Unit].sql, + "(foo.bar NOT IN ((?,?), (?,?))) ") + assertEquals( + notIn(sql"foo.bar", 3 -> "three", 4 -> "four", 5 -> "five").query[Unit].sql, + "(foo.bar NOT IN ((?,?), (?,?), (?,?))) ") + } + test("notIn (3-column, varargs)") { + assertEquals( + notIn(sql"foo.bar", (1.2, "A", 3), (4.5, "B", 6)).query[Unit].sql, + "(foo.bar NOT IN ((?,?,?), (?,?,?))) ") + assertEquals( + notIn(sql"foo.bar", (9.8, "Z", 7), (6.5, "Y", 4), (3.2, "X", 1)).query[Unit].sql, + "(foo.bar NOT IN ((?,?,?), (?,?,?), (?,?,?))) ") } - test("inValues for one column") { + test("in (1-column)") { assertEquals( - inValues(fr0"foo.bar", nelInt).query[Unit].sql, - "foo.bar IN (?, ?, ?) ") + in(fr0"foo.bar", nelInt).query[Unit].sql, + "(foo.bar IN (?, ?, ?)) ") } - test("inValues for two columns") { + test("in (2-columns)") { assertEquals( - inValues(fr0"foo.bar", nelIntBool2).query[Unit].sql, - "foo.bar IN ((?,?), (?,?)) ") + in(fr0"foo.bar", nelIntBool2).query[Unit].sql, + "(foo.bar IN ((?,?), (?,?))) ") } - test("inValues for three columns") { + test("in (3-columns)") { assertEquals( - inValues(fr0"foo.bar", nelStrDblInt3).query[Unit].sql, - "foo.bar IN ((?,?,?), (?,?,?), (?,?,?)) ") + in(fr0"foo.bar", nelStrDblInt3).query[Unit].sql, + "(foo.bar IN ((?,?,?), (?,?,?), (?,?,?))) ") } - test("notInValues for one column") { + test("inOpt (1-column, many)") { assertEquals( - notInValues(fr0"foo.bar", nelInt).query[Unit].sql, - "foo.bar NOT IN (?, ?, ?) ") + inOpt(fr0"foo.bar", listInt3).map(_.query[Unit].sql), + Some("(foo.bar IN (?, ?, ?)) ")) } - test("notInValues for two columns") { + test("inOpt (2-columns, many)") { assertEquals( - notInValues(fr0"foo.bar", nelIntBool2).query[Unit].sql, - "foo.bar NOT IN ((?,?), (?,?)) ") + inOpt(fr0"foo.bar", listIntBool2).map(_.query[Unit].sql), + Some("(foo.bar IN ((?,?), (?,?))) ")) } - test("notInValues for three columns") { + test("inOpt (3-columns, many)") { assertEquals( - notInValues(fr0"foo.bar", nelStrDblInt3).query[Unit].sql, - "foo.bar NOT IN ((?,?,?), (?,?,?), (?,?,?)) ") + inOpt(fr0"foo.bar", listStrDblInt3).map(_.query[Unit].sql), + Some("(foo.bar IN ((?,?,?), (?,?,?), (?,?,?))) ")) } - - test("notIn (varargs many)") { - assertEquals(notIn(sql"foo", 1, 2, 3).query[Unit].sql, "(foo NOT IN (? , ? , ? ) ) ") + test("inOpt (empty)") { + assert(inOpt(fr0"foo.bar", List.empty[Int]).isEmpty, "1 column") + assert(inOpt(fr0"foo.bar", List.empty[(Int, Boolean)]).isEmpty, "2 columns") + assert(inOpt(fr0"foo.bar", List.empty[(String, Double, Int)]).isEmpty, "3 columns") } - test("notIn (Reducible 1)") { - assertEquals(notIn(sql"foo", NonEmptyList.of(1)).query[Unit].sql, "(foo NOT IN (? ) ) ") + test("notIn (1-column)") { + assertEquals( + notIn(fr0"foo.bar", nelInt).query[Unit].sql, + "(foo.bar NOT IN (?, ?, ?)) ") } - - test("notIn (Reducible many)") { - assertEquals(notIn(sql"foo", nelInt).query[Unit].sql, "(foo NOT IN (? , ? , ? ) ) ") + test("notIn (2-columns)") { + assertEquals( + notIn(fr0"foo.bar", nelIntBool2).query[Unit].sql, + "(foo.bar NOT IN ((?,?), (?,?))) ") } - - test("notInOpt (Foldable empty)") { - assertEquals(notInOpt(sql"foo", List.empty[Int]).map(_.query[Unit].sql), None) + test("notIn (3-columns)") { + assertEquals( + notIn(fr0"foo.bar", nelStrDblInt3).query[Unit].sql, + "(foo.bar NOT IN ((?,?,?), (?,?,?), (?,?,?))) ") } - test("notInOpt (Foldable 1)") { - assertEquals(notInOpt(sql"foo", List(1)).map(_.query[Unit].sql), Some("(foo NOT IN (? ) ) ")) + test("notInOpt (1-column, many)") { + assertEquals( + notInOpt(fr0"foo.bar", listInt3).map(_.query[Unit].sql), + Some("(foo.bar NOT IN (?, ?, ?)) ")) } - - test("notInOpt (Foldable many)") { - assertEquals(notInOpt(sql"foo", listInt).map(_.query[Unit].sql), Some("(foo NOT IN (? , ? , ? ) ) ")) + test("notInOpt (2-columns, many)") { + assertEquals( + notInOpt(fr0"foo.bar", listIntBool2).map(_.query[Unit].sql), + Some("(foo.bar NOT IN ((?,?), (?,?))) ")) + } + test("notInOpt (3-columns, many)") { + assertEquals( + notInOpt(fr0"foo.bar", listStrDblInt3).map(_.query[Unit].sql), + Some("(foo.bar NOT IN ((?,?,?), (?,?,?), (?,?,?))) ")) + } + test("notInOpt (empty)") { + assert(notInOpt(fr0"foo.bar", List.empty[Int]).isEmpty, "1 column") + assert(notInOpt(fr0"foo.bar", List.empty[(Int, Boolean)]).isEmpty, "2 columns") + assert(notInOpt(fr0"foo.bar", List.empty[(String, Double, Int)]).isEmpty, "3 columns") } test("and (vararg 2)") {