From f68bd5bea3281cc31b577a0bbf169b4c3209c408 Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Tue, 3 Oct 2023 01:02:48 -0600 Subject: [PATCH] feat!: Fatal warnings Fix all warnings and enable fatal warnings. Also remove the dependency on scala-compat. This is mostly backwards compatible, but won't work with versions of scala before 2.13. It also adds an implicit conversion from SqlRow => A to RowParser[A], which allows continuing to pass in a function as the argument instead of an implicit RowParser, but might be subtly different in some cases. --- build.sbt | 5 +- .../open/relate/macros/package.scala | 4 +- .../open/relate/macros/RowParserTest.scala | 2 +- .../lucidchart/relate/postgres/package.scala | 6 +- relate/build.sbt | 1 - .../lucidchart/relate/CollectionsParser.scala | 12 +-- .../lucidchart/relate/CollectionsSql.scala | 18 ++--- .../relate/CollectionsSqlResult.scala | 10 +-- .../lucidchart/relate/Parameterizable.scala | 62 ++++++++------- .../com/lucidchart/relate/Parameters.scala | 78 ++++++++++++------- .../com/lucidchart/relate/RowIterator.scala | 4 +- .../com/lucidchart/relate/RowParser.scala | 46 +++++------ .../com/lucidchart/relate/SqlQuery.scala | 16 ++-- .../com/lucidchart/relate/SqlResult.scala | 40 ++++------ .../scala/com/lucidchart/relate/SqlRow.scala | 8 +- .../lucidchart/relate/StatementPreparer.scala | 2 +- relate/src/test/scala/ColReaderTest.scala | 4 +- .../src/test/scala/ImplicitParsingTest.scala | 4 +- relate/src/test/scala/RelateITSpec.scala | 6 +- relate/src/test/scala/SqlResultSpec.scala | 8 +- 20 files changed, 180 insertions(+), 156 deletions(-) diff --git a/build.sbt b/build.sbt index d2bd6f4..a23392e 100644 --- a/build.sbt +++ b/build.sbt @@ -40,7 +40,8 @@ inScope(Global)(Seq( licenses += "Apache License 2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0"), scalacOptions ++= Seq( "-deprecation", - "-feature" + "-feature", + "-Werror" ), scmInfo := Some(ScmInfo(url("https://github.com/lucidsoftware/relate"), "scm:git:git@github.com:lucidsoftware/relate.git")), Benchmark / test / tags += benchmarkTag -> 1, @@ -49,5 +50,5 @@ inScope(Global)(Seq( version := sys.props.getOrElse("build.version", "0-SNAPSHOT") )) -skip in publish := true +publish / skip := true publishTo := sonatypePublishToBundle.value diff --git a/macros/src/main/scala/com/lucidchart/open/relate/macros/package.scala b/macros/src/main/scala/com/lucidchart/open/relate/macros/package.scala index 2abb70b..234bbd9 100644 --- a/macros/src/main/scala/com/lucidchart/open/relate/macros/package.scala +++ b/macros/src/main/scala/com/lucidchart/open/relate/macros/package.scala @@ -16,8 +16,8 @@ package object macros { @implicitNotFound("A value of type ${A} is never allowed as an RecordOption") private trait RecordOptionValue[A] private object RecordOptionValue { - implicit val bool = new RecordOptionValue[Boolean] {} - implicit val map = new RecordOptionValue[Map[String, String]] {} + implicit val bool: RecordOptionValue[Boolean] = new RecordOptionValue[Boolean] {} + implicit val map: RecordOptionValue[Map[String, String]] = new RecordOptionValue[Map[String, String]] {} } case class RecordOption[A: RecordOptionValue] private (key: String, value: A) diff --git a/macros/src/test/scala/com/lucidchart/open/relate/macros/RowParserTest.scala b/macros/src/test/scala/com/lucidchart/open/relate/macros/RowParserTest.scala index 54ffeec..aaeeca4 100644 --- a/macros/src/test/scala/com/lucidchart/open/relate/macros/RowParserTest.scala +++ b/macros/src/test/scala/com/lucidchart/open/relate/macros/RowParserTest.scala @@ -239,7 +239,7 @@ class RowParserTest extends Specification with Mockito { "fail to compile for anything besides a case class" in { illTyped("@Record() class Thing", "@Record must be used on a case class") - illTyped("@Record() def f()", "@Record must be used on a case class") + illTyped("@Record() def f(): Unit", "@Record must be used on a case class") illTyped("@Record() object Thing {}", "@Record must be used on a case class") illTyped("@Record() trait Thing", "@Record must be used on a case class") diff --git a/postgres/src/main/scala/com/lucidchart/relate/postgres/package.scala b/postgres/src/main/scala/com/lucidchart/relate/postgres/package.scala index c1e58db..c7382fa 100644 --- a/postgres/src/main/scala/com/lucidchart/relate/postgres/package.scala +++ b/postgres/src/main/scala/com/lucidchart/relate/postgres/package.scala @@ -6,10 +6,10 @@ import scala.concurrent.duration.FiniteDuration package object postgres { - implicit val pgIntervalParameterizable = + implicit val pgIntervalParameterizable: Parameterizable[PGInterval] = Parameterizable(_.setObject(_, _: PGInterval), _.setNull(_, Types.JAVA_OBJECT)) - implicit val finiteDurationParameterizable = - Parameterizable.from((value: FiniteDuration) => new PGInterval(0, 0, 0, 0, 0, value.toSeconds)) + implicit val finiteDurationParameterizable: Parameterizable[FiniteDuration] = + Parameterizable.from((value: FiniteDuration) => new PGInterval(0, 0, 0, 0, 0, value.toSeconds.toDouble)) } diff --git a/relate/build.sbt b/relate/build.sbt index b50904b..783d02e 100644 --- a/relate/build.sbt +++ b/relate/build.sbt @@ -12,7 +12,6 @@ libraryDependencies ++= Seq( "com.h2database" % "h2" % "1.4.191" % "test", "com.storm-enroute" %% "scalameter" % "0.19" % Benchmark, "com.storm-enroute" %% "scalameter" % "0.19" % Regression, - "org.scala-lang.modules" %% "scala-collection-compat" % "2.1.6" ) libraryDependencies ++= (CrossVersion.binaryScalaVersion(scalaVersion.value) match { diff --git a/relate/src/main/scala/com/lucidchart/relate/CollectionsParser.scala b/relate/src/main/scala/com/lucidchart/relate/CollectionsParser.scala index c01a393..5f14b1c 100644 --- a/relate/src/main/scala/com/lucidchart/relate/CollectionsParser.scala +++ b/relate/src/main/scala/com/lucidchart/relate/CollectionsParser.scala @@ -1,6 +1,6 @@ package com.lucidchart.relate -import scala.collection.compat._ +import scala.collection.Factory import scala.language.higherKinds trait CollectionsParser { @@ -14,19 +14,19 @@ trait CollectionsParser { } } - builder.result + builder.result() } - implicit def option[B: RowParser] = RowParser[Option[B]] { result => + implicit def option[B: RowParser]: RowParser[Option[B]] = RowParser[Option[B]] { result => limitedCollection[B, List](1).parse(result).headOption } - implicit def collection[B: RowParser, Col[_]](implicit factory: Factory[B, Col[B]]) = + implicit def collection[B: RowParser, Col[_]](implicit factory: Factory[B, Col[B]]): RowParser[Col[B]] = limitedCollection[B, Col](Long.MaxValue) implicit def pairCollection[Key: RowParser, Value: RowParser, PairCol[_, _]](implicit factory: Factory[(Key, Value), PairCol[Key, Value]] - ) = + ): RowParser[PairCol[Key, Value]] = RowParser { result => val builder = factory.newBuilder @@ -37,6 +37,6 @@ trait CollectionsParser { } } - builder.result + builder.result() } } diff --git a/relate/src/main/scala/com/lucidchart/relate/CollectionsSql.scala b/relate/src/main/scala/com/lucidchart/relate/CollectionsSql.scala index a9e23db..6b21027 100644 --- a/relate/src/main/scala/com/lucidchart/relate/CollectionsSql.scala +++ b/relate/src/main/scala/com/lucidchart/relate/CollectionsSql.scala @@ -1,7 +1,7 @@ package com.lucidchart.relate import java.sql.{Connection, PreparedStatement, ResultSet} -import scala.collection.compat._ +import scala.collection.Factory import scala.language.higherKinds /** @@ -51,9 +51,9 @@ trait CollectionsSql { self: Sql => * @return * the results as an arbitrary collection of records */ - def asCollection[U, T[_]](parser: SqlRow => U)(implicit factory: Factory[U, T[U]], connection: Connection): T[U] = - normalStatement.execute(_.asCollection(parser)) - def asCollection[U: RowParser, T[_]]()(implicit factory: Factory[U, T[U]], connection: Connection): T[U] = + // def asCollection[U, T[_]](parser: SqlRow => U)(implicit factory: Factory[U, T[U]], connection: Connection): T[U] = + // normalStatement.execute(_.asCollection(parser)) + def asCollection[U: RowParser, T[_]](implicit factory: Factory[U, T[U]], connection: Connection): T[U] = normalStatement.execute(_.asCollection[U, T]) /** @@ -65,11 +65,11 @@ trait CollectionsSql { self: Sql => * @return * the results as an arbitrary collection of key value pairs */ - def asPairCollection[U, V, T[_, _]]( - parser: SqlRow => (U, V) - )(implicit factory: Factory[(U, V), T[U, V]], connection: Connection): T[U, V] = - normalStatement.execute(_.asPairCollection(parser)) - def asPairCollection[U, V, T[_, _]]()(implicit + // def asPairCollection[U, V, T[_, _]]( + // parser: SqlRow => (U, V) + // )(implicit factory: Factory[(U, V), T[U, V]], connection: Connection): T[U, V] = + // normalStatement.execute(_.asPairCollection(parser)) + def asPairCollection[U, V, T[_, _]](implicit factory: Factory[(U, V), T[U, V]], connection: Connection, p: RowParser[(U, V)] diff --git a/relate/src/main/scala/com/lucidchart/relate/CollectionsSqlResult.scala b/relate/src/main/scala/com/lucidchart/relate/CollectionsSqlResult.scala index eac76bc..c40e1e5 100644 --- a/relate/src/main/scala/com/lucidchart/relate/CollectionsSqlResult.scala +++ b/relate/src/main/scala/com/lucidchart/relate/CollectionsSqlResult.scala @@ -1,7 +1,7 @@ package com.lucidchart.relate import java.sql.ResultSetMetaData -import scala.collection.compat._ +import scala.collection.Factory import scala.collection.mutable import scala.language.higherKinds @@ -9,7 +9,7 @@ trait CollectionsSqlResult { self: SqlResult => def asCollection[U, T[_]](parser: SqlRow => U)(implicit factory: Factory[U, T[U]]): T[U] = asCollection(parser, Long.MaxValue) - def asCollection[U: RowParser, T[_]]()(implicit factory: Factory[U, T[U]]): T[U] = + def asCollection[U: RowParser, T[_]](implicit factory: Factory[U, T[U]]): T[U] = asCollection(implicitly[RowParser[U]].parse, Long.MaxValue) protected def asCollection[U: RowParser, T[_]](maxRows: Long)(implicit factory: Factory[U, T[U]]): T[U] = asCollection(implicitly[RowParser[U]].parse, maxRows) @@ -22,10 +22,10 @@ trait CollectionsSqlResult { self: SqlResult => } } - builder.result + builder.result() } - def asPairCollection[U, V, T[_, _]]()(implicit p: RowParser[(U, V)], factory: Factory[(U, V), T[U, V]]): T[U, V] = { + def asPairCollection[U, V, T[_, _]](implicit p: RowParser[(U, V)], factory: Factory[(U, V), T[U, V]]): T[U, V] = { asPairCollection(p.parse, Long.MaxValue) } def asPairCollection[U, V, T[_, _]](parser: SqlRow => (U, V))(implicit factory: Factory[(U, V), T[U, V]]): T[U, V] = @@ -45,7 +45,7 @@ trait CollectionsSqlResult { self: SqlResult => } } - builder.result + builder.result() } } diff --git a/relate/src/main/scala/com/lucidchart/relate/Parameterizable.scala b/relate/src/main/scala/com/lucidchart/relate/Parameterizable.scala index 0e9f5ce..8bb82dc 100644 --- a/relate/src/main/scala/com/lucidchart/relate/Parameterizable.scala +++ b/relate/src/main/scala/com/lucidchart/relate/Parameterizable.scala @@ -12,12 +12,12 @@ trait Parameterizable[-A] { /** * Set the parameterized value at index {@code i} in the prepared statement to the {@code value}. */ - def set(statement: PreparedStatement, i: Int, value: A) + def set(statement: PreparedStatement, i: Int, value: A): Unit /** * Set the parameterized value at index {@code i} in the prepared statement to {@code null}. */ - def setNull(statement: PreparedStatement, i: Int) + def setNull(statement: PreparedStatement, i: Int): Unit final def setOption(statement: PreparedStatement, i: Int, value: Option[A]) = value.fold(setNull(statement, i))(set(statement, i, _)) } @@ -39,38 +39,42 @@ object Parameterizable { def from[A, B: Parameterizable](f: A => B) = implicitly[Parameterizable[B]].contraMap(f) - implicit val array = apply(_.setArray(_, _: Array), _.setNull(_, Types.ARRAY)) + implicit val array: Parameterizable[Array] = apply(_.setArray(_, _: Array), _.setNull(_, Types.ARRAY)) // ideally, this would be named jBigDecimal, but that wouldn't be backwards compatibility - implicit val bigDecimal = apply(_.setBigDecimal(_, _: java.math.BigDecimal), _.setNull(_, Types.DECIMAL)) - implicit val scalaBigDecimal = apply( + implicit val bigDecimal: Parameterizable[java.math.BigDecimal] = + apply(_.setBigDecimal(_, _: java.math.BigDecimal), _.setNull(_, Types.DECIMAL)) + implicit val scalaBigDecimal: Parameterizable[scala.math.BigDecimal] = apply( (stmt: PreparedStatement, i: Int, v: scala.math.BigDecimal) => stmt.setBigDecimal(i, v.bigDecimal), _.setNull(_, Types.DECIMAL) ) - implicit val blob = apply(_.setBlob(_, _: Blob), _.setNull(_, Types.BLOB)) - implicit val boolean = apply(_.setBoolean(_, _: Boolean), _.setNull(_, Types.BOOLEAN)) - implicit val byte = apply(_.setByte(_, _: Byte), _.setNull(_, Types.TINYINT)) - implicit val bytes = apply(_.setBytes(_, _: scala.Array[Byte]), _.setNull(_, Types.VARBINARY)) - implicit val clob = apply(_.setClob(_, _: Clob), _.setNull(_, Types.CLOB)) - implicit val date = apply(_.setDate(_, _: Date), _.setNull(_, Types.DATE)) - implicit val double = apply(_.setDouble(_, _: Double), _.setNull(_, Types.DOUBLE)) - implicit val float = apply(_.setFloat(_, _: Float), _.setNull(_, Types.FLOAT)) - implicit val int = apply(_.setInt(_, _: Int), _.setNull(_, Types.INTEGER)) - implicit val long = apply(_.setLong(_, _: Long), _.setNull(_, Types.BIGINT)) - implicit val nClob = apply(_.setNClob(_, _: NClob), _.setNull(_, Types.NCLOB)) - implicit val ref = apply(_.setRef(_, _: Ref), _.setNull(_, Types.REF)) - implicit val rowId = apply(_.setRowId(_, _: RowId), _.setNull(_, Types.ROWID)) - implicit val short = apply(_.setShort(_, _: Short), _.setNull(_, Types.SMALLINT)) - implicit val sqlXml = apply(_.setSQLXML(_, _: SQLXML), _.setNull(_, Types.SQLXML)) - implicit val string = apply(_.setString(_, _: String), _.setNull(_, Types.VARCHAR)) - implicit val time = apply(_.setTime(_, _: Time), _.setNull(_, Types.TIME)) - implicit val timestamp = apply(_.setTimestamp(_, _: Timestamp), _.setNull(_, Types.TIMESTAMP)) - implicit val url = apply(_.setURL(_, _: URL), _.setNull(_, Types.DATALINK)) + implicit val blob: Parameterizable[Blob] = apply(_.setBlob(_, _: Blob), _.setNull(_, Types.BLOB)) + implicit val boolean: Parameterizable[Boolean] = apply(_.setBoolean(_, _: Boolean), _.setNull(_, Types.BOOLEAN)) + implicit val byte: Parameterizable[Byte] = apply(_.setByte(_, _: Byte), _.setNull(_, Types.TINYINT)) + implicit val bytes: Parameterizable[scala.Array[Byte]] = + apply(_.setBytes(_, _: scala.Array[Byte]), _.setNull(_, Types.VARBINARY)) + implicit val clob: Parameterizable[Clob] = apply(_.setClob(_, _: Clob), _.setNull(_, Types.CLOB)) + implicit val date: Parameterizable[Date] = apply(_.setDate(_, _: Date), _.setNull(_, Types.DATE)) + implicit val double: Parameterizable[Double] = apply(_.setDouble(_, _: Double), _.setNull(_, Types.DOUBLE)) + implicit val float: Parameterizable[Float] = apply(_.setFloat(_, _: Float), _.setNull(_, Types.FLOAT)) + implicit val int: Parameterizable[Int] = apply(_.setInt(_, _: Int), _.setNull(_, Types.INTEGER)) + implicit val long: Parameterizable[Long] = apply(_.setLong(_, _: Long), _.setNull(_, Types.BIGINT)) + implicit val nClob: Parameterizable[NClob] = apply(_.setNClob(_, _: NClob), _.setNull(_, Types.NCLOB)) + implicit val ref: Parameterizable[Ref] = apply(_.setRef(_, _: Ref), _.setNull(_, Types.REF)) + implicit val rowId: Parameterizable[RowId] = apply(_.setRowId(_, _: RowId), _.setNull(_, Types.ROWID)) + implicit val short: Parameterizable[Short] = apply(_.setShort(_, _: Short), _.setNull(_, Types.SMALLINT)) + implicit val sqlXml: Parameterizable[SQLXML] = apply(_.setSQLXML(_, _: SQLXML), _.setNull(_, Types.SQLXML)) + implicit val string: Parameterizable[String] = apply(_.setString(_, _: String), _.setNull(_, Types.VARCHAR)) + implicit val time: Parameterizable[Time] = apply(_.setTime(_, _: Time), _.setNull(_, Types.TIME)) + implicit val timestamp: Parameterizable[Timestamp] = + apply(_.setTimestamp(_, _: Timestamp), _.setNull(_, Types.TIMESTAMP)) + implicit val url: Parameterizable[URL] = apply(_.setURL(_, _: URL), _.setNull(_, Types.DATALINK)) - implicit val javaDate = from((date: java.util.Date) => new Timestamp(date.getTime)) - implicit val localDate = from(Date.valueOf(_: LocalDate)) - implicit val localTime = from(Time.valueOf(_: LocalTime)) - implicit val instant = from(Timestamp.from) - implicit val uuid = from { uuid: UUID => + // val foo = implicitly[Parameterizable[Timestamp]] + implicit val javaDate: Parameterizable[java.util.Date] = timestamp.contraMap(d => new Timestamp(d.getTime)) + implicit val localDate: Parameterizable[LocalDate] = date.contraMap(Date.valueOf) + implicit val localTime: Parameterizable[LocalTime] = time.contraMap(Time.valueOf) + implicit val instant: Parameterizable[java.time.Instant] = timestamp.contraMap(Timestamp.from) + implicit val uuid: Parameterizable[UUID] = from { uuid: UUID => val bb = ByteBuffer.wrap(new scala.Array[Byte](16)) bb.putLong(uuid.getMostSignificantBits) bb.putLong(uuid.getLeastSignificantBits) diff --git a/relate/src/main/scala/com/lucidchart/relate/Parameters.scala b/relate/src/main/scala/com/lucidchart/relate/Parameters.scala index 45fbac1..d1c01e5 100644 --- a/relate/src/main/scala/com/lucidchart/relate/Parameters.scala +++ b/relate/src/main/scala/com/lucidchart/relate/Parameters.scala @@ -31,7 +31,7 @@ import scala.language.implicitConversions */ trait Parameter { - def appendPlaceholders(stringBuilder: StringBuilder) + def appendPlaceholders(stringBuilder: StringBuilder): Unit def parameterize(statement: PreparedStatement, i: Int): Int } @@ -46,33 +46,52 @@ object Parameter { } type SP[T] = T => SingleParameter - implicit def fromByteArray(it: Array[Byte]) = single(it) - implicit def fromArray[A](it: Array[A])(implicit sp: SP[A]) = new TupleParameter(it.map(sp)) - implicit def fromIterable[A](it: Iterable[A])(implicit sp: SP[A]) = new TupleParameter(it.map(sp)) - implicit def fromTuple1[T1](t: Tuple1[T1])(implicit sp1: SP[T1]) = TupleParameter(sp1(t._1)) - implicit def fromTuple2[T1, T2](t: Tuple2[T1, T2])(implicit sp1: SP[T1], sp2: SP[T2]) = + implicit def fromByteArray(it: Array[Byte]): SingleParameter = single(it) + implicit def fromArray[A](it: Array[A])(implicit sp: SP[A]): TupleParameter = new TupleParameter(it.map(sp)) + implicit def fromIterable[A](it: Iterable[A])(implicit sp: SP[A]): TupleParameter = new TupleParameter(it.map(sp)) + implicit def fromTuple1[T1](t: Tuple1[T1])(implicit sp1: SP[T1]): TupleParameter = TupleParameter(sp1(t._1)) + implicit def fromTuple2[T1, T2](t: Tuple2[T1, T2])(implicit sp1: SP[T1], sp2: SP[T2]): TupleParameter = TupleParameter(sp1(t._1), sp2(t._2)) - implicit def fromTuple3[T1, T2, T3](t: Tuple3[T1, T2, T3])(implicit sp1: SP[T1], sp2: SP[T2], sp3: SP[T3]) = + implicit def fromTuple3[T1, T2, T3]( + t: Tuple3[T1, T2, T3] + )(implicit sp1: SP[T1], sp2: SP[T2], sp3: SP[T3]): TupleParameter = TupleParameter(sp1(t._1), sp2(t._2), sp3(t._3)) implicit def fromTuple4[T1, T2, T3, T4]( t: Tuple4[T1, T2, T3, T4] - )(implicit sp1: SP[T1], sp2: SP[T2], sp3: SP[T3], sp4: SP[T4]) = + )(implicit sp1: SP[T1], sp2: SP[T2], sp3: SP[T3], sp4: SP[T4]): TupleParameter = TupleParameter(sp1(t._1), sp2(t._2), sp3(t._3), sp4(t._4)) implicit def fromTuple5[T1, T2, T3, T4, T5]( t: Tuple5[T1, T2, T3, T4, T5] - )(implicit sp1: SP[T1], sp2: SP[T2], sp3: SP[T3], sp4: SP[T4], sp5: SP[T5]) = + )(implicit sp1: SP[T1], sp2: SP[T2], sp3: SP[T3], sp4: SP[T4], sp5: SP[T5]): TupleParameter = TupleParameter(sp1(t._1), sp2(t._2), sp3(t._3), sp4(t._4), sp5(t._5)) implicit def fromTuple6[T1, T2, T3, T4, T5, T6]( t: Tuple6[T1, T2, T3, T4, T5, T6] - )(implicit sp1: SP[T1], sp2: SP[T2], sp3: SP[T3], sp4: SP[T4], sp5: SP[T5], sp6: SP[T6]) = + )(implicit sp1: SP[T1], sp2: SP[T2], sp3: SP[T3], sp4: SP[T4], sp5: SP[T5], sp6: SP[T6]): TupleParameter = TupleParameter(sp1(t._1), sp2(t._2), sp3(t._3), sp4(t._4), sp5(t._5), sp6(t._6)) implicit def fromTuple7[T1, T2, T3, T4, T5, T6, T7]( t: Tuple7[T1, T2, T3, T4, T5, T6, T7] - )(implicit sp1: SP[T1], sp2: SP[T2], sp3: SP[T3], sp4: SP[T4], sp5: SP[T5], sp6: SP[T6], sp7: SP[T7]) = + )(implicit + sp1: SP[T1], + sp2: SP[T2], + sp3: SP[T3], + sp4: SP[T4], + sp5: SP[T5], + sp6: SP[T6], + sp7: SP[T7] + ): TupleParameter = TupleParameter(sp1(t._1), sp2(t._2), sp3(t._3), sp4(t._4), sp5(t._5), sp6(t._6), sp7(t._7)) implicit def fromTuple8[T1, T2, T3, T4, T5, T6, T7, T8]( t: Tuple8[T1, T2, T3, T4, T5, T6, T7, T8] - )(implicit sp1: SP[T1], sp2: SP[T2], sp3: SP[T3], sp4: SP[T4], sp5: SP[T5], sp6: SP[T6], sp7: SP[T7], sp8: SP[T8]) = + )(implicit + sp1: SP[T1], + sp2: SP[T2], + sp3: SP[T3], + sp4: SP[T4], + sp5: SP[T5], + sp6: SP[T6], + sp7: SP[T7], + sp8: SP[T8] + ): TupleParameter = TupleParameter(sp1(t._1), sp2(t._2), sp3(t._3), sp4(t._4), sp5(t._5), sp6(t._6), sp7(t._7), sp8(t._8)) implicit def fromTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9](t: Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9])(implicit sp1: SP[T1], @@ -84,7 +103,8 @@ object Parameter { sp7: SP[T7], sp8: SP[T8], sp9: SP[T9] - ) = TupleParameter(sp1(t._1), sp2(t._2), sp3(t._3), sp4(t._4), sp5(t._5), sp6(t._6), sp7(t._7), sp8(t._8), sp9(t._9)) + ): TupleParameter = + TupleParameter(sp1(t._1), sp2(t._2), sp3(t._3), sp4(t._4), sp5(t._5), sp6(t._6), sp7(t._7), sp8(t._8), sp9(t._9)) implicit def fromTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]( t: Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] )(implicit @@ -98,7 +118,7 @@ object Parameter { sp8: SP[T8], sp9: SP[T9], sp10: SP[T10] - ) = TupleParameter( + ): TupleParameter = TupleParameter( sp1(t._1), sp2(t._2), sp3(t._3), @@ -124,7 +144,7 @@ object Parameter { sp9: SP[T9], sp10: SP[T10], sp11: SP[T11] - ) = TupleParameter( + ): TupleParameter = TupleParameter( sp1(t._1), sp2(t._2), sp3(t._3), @@ -152,7 +172,7 @@ object Parameter { sp10: SP[T10], sp11: SP[T11], sp12: SP[T12] - ) = TupleParameter( + ): TupleParameter = TupleParameter( sp1(t._1), sp2(t._2), sp3(t._3), @@ -182,7 +202,7 @@ object Parameter { sp11: SP[T11], sp12: SP[T12], sp13: SP[T13] - ) = TupleParameter( + ): TupleParameter = TupleParameter( sp1(t._1), sp2(t._2), sp3(t._3), @@ -214,7 +234,7 @@ object Parameter { sp12: SP[T12], sp13: SP[T13], sp14: SP[T14] - ) = TupleParameter( + ): TupleParameter = TupleParameter( sp1(t._1), sp2(t._2), sp3(t._3), @@ -248,7 +268,7 @@ object Parameter { sp13: SP[T13], sp14: SP[T14], sp15: SP[T15] - ) = TupleParameter( + ): TupleParameter = TupleParameter( sp1(t._1), sp2(t._2), sp3(t._3), @@ -284,7 +304,7 @@ object Parameter { sp14: SP[T14], sp15: SP[T15], sp16: SP[T16] - ) = TupleParameter( + ): TupleParameter = TupleParameter( sp1(t._1), sp2(t._2), sp3(t._3), @@ -322,7 +342,7 @@ object Parameter { sp15: SP[T15], sp16: SP[T16], sp17: SP[T17] - ) = TupleParameter( + ): TupleParameter = TupleParameter( sp1(t._1), sp2(t._2), sp3(t._3), @@ -362,7 +382,7 @@ object Parameter { sp16: SP[T16], sp17: SP[T17], sp18: SP[T18] - ) = TupleParameter( + ): TupleParameter = TupleParameter( sp1(t._1), sp2(t._2), sp3(t._3), @@ -404,7 +424,7 @@ object Parameter { sp17: SP[T17], sp18: SP[T18], sp19: SP[T19] - ) = TupleParameter( + ): TupleParameter = TupleParameter( sp1(t._1), sp2(t._2), sp3(t._3), @@ -448,7 +468,7 @@ object Parameter { sp18: SP[T18], sp19: SP[T19], sp20: SP[T20] - ) = TupleParameter( + ): TupleParameter = TupleParameter( sp1(t._1), sp2(t._2), sp3(t._3), @@ -514,7 +534,7 @@ object Parameter { sp19: SP[T19], sp20: SP[T20], sp21: SP[T21] - ) = TupleParameter( + ): TupleParameter = TupleParameter( sp1(t._1), sp2(t._2), sp3(t._3), @@ -585,7 +605,7 @@ object Parameter { sp20: SP[T20], sp21: SP[T21], sp22: SP[T22] - ) = TupleParameter( + ): TupleParameter = TupleParameter( sp1(t._1), sp2(t._2), sp3(t._3), @@ -610,11 +630,13 @@ object Parameter { sp22(t._22) ) - implicit def fromTuples[A](seq: Seq[A])(implicit tp: A => TupleParameter) = new TuplesParameter(seq.map(tp)) + implicit def fromTuples[A](seq: Seq[A])(implicit tp: A => TupleParameter): TuplesParameter = new TuplesParameter( + seq.map(tp) + ) } trait SingleParameter extends Parameter { - protected[this] def set(statement: PreparedStatement, i: Int) + protected[this] def set(statement: PreparedStatement, i: Int): Unit def appendPlaceholders(stringBuilder: StringBuilder) = stringBuilder.append("?") def parameterize(statement: PreparedStatement, i: Int) = { diff --git a/relate/src/main/scala/com/lucidchart/relate/RowIterator.scala b/relate/src/main/scala/com/lucidchart/relate/RowIterator.scala index 1c1d952..f40ea74 100644 --- a/relate/src/main/scala/com/lucidchart/relate/RowIterator.scala +++ b/relate/src/main/scala/com/lucidchart/relate/RowIterator.scala @@ -15,7 +15,7 @@ private[relate] class RowIterator[A](parser: SqlRow => A, stmt: PreparedStatemen /** * Make certain that all resources are closed */ - override def finalize() { + override def finalize(): Unit = { close() } @@ -24,7 +24,7 @@ private[relate] class RowIterator[A](parser: SqlRow => A, stmt: PreparedStatemen * @return * whether there is another row */ - override def hasNext(): Boolean = _hasNext + override def hasNext: Boolean = _hasNext /** * Parse the next row using the RowParser passed into the class diff --git a/relate/src/main/scala/com/lucidchart/relate/RowParser.scala b/relate/src/main/scala/com/lucidchart/relate/RowParser.scala index 8306118..b65a545 100644 --- a/relate/src/main/scala/com/lucidchart/relate/RowParser.scala +++ b/relate/src/main/scala/com/lucidchart/relate/RowParser.scala @@ -4,6 +4,7 @@ import java.util.Date import java.time.Instant import scala.collection.mutable import scala.language.higherKinds +import scala.language.implicitConversions trait RowParser[A] extends (SqlRow => A) { def parse(row: SqlRow): A @@ -16,33 +17,34 @@ object RowParser extends CollectionsParser { def parse(row: SqlRow) = f(row) } - def bigInt(columnLabel: String) = RowParser(_.apply[BigInt](columnLabel)) - def date(columnLabel: String) = RowParser(_.apply[Date](columnLabel)) - def instant(columnLabel: String) = RowParser(_.apply[Instant](columnLabel)) - def int(columnLabel: String) = RowParser(_.apply[Int](columnLabel)) - def long(columnLabel: String) = RowParser(_.apply[Long](columnLabel)) - def string(columnLabel: String) = RowParser(_.apply[String](columnLabel)) + def bigInt(columnLabel: String): RowParser[BigInt] = r => r.apply[BigInt](columnLabel) + def date(columnLabel: String): RowParser[Date] = r => r.apply[Date](columnLabel) + def instant(columnLabel: String): RowParser[Instant] = r => r.apply[Instant](columnLabel) + def int(columnLabel: String): RowParser[Int] = r => r.apply[Int](columnLabel) + def long(columnLabel: String): RowParser[Long] = r => r.apply[Long](columnLabel) + def string(columnLabel: String): RowParser[String] = r => r.apply[String](columnLabel) - private[relate] val insertInt = (row: SqlRow) => row.strictInt(1) - private[relate] val insertLong = (row: SqlRow) => row.strictLong(1) + private[relate] val insertInt: RowParser[Int] = row => row.strictInt(1) + private[relate] val insertLong: RowParser[Long] = row => row.strictLong(1) - implicit def multiMap[Key: RowParser, Value: RowParser] = RowParser[Map[Key, Set[Value]]] { result => - val mm: mutable.Map[Key, Set[Value]] = new mutable.HashMap[Key, Set[Value]] + implicit def multiMap[Key: RowParser, Value: RowParser]: RowParser[Map[Key, Set[Value]]] = + RowParser[Map[Key, Set[Value]]] { result => + val mm = new mutable.HashMap[Key, mutable.Builder[Value, Set[Value]]] - result.withResultSet { resultSet => - while (resultSet.next()) { - val key = implicitly[RowParser[Key]].parse(result) - val value = implicitly[RowParser[Value]].parse(result) + result.withResultSet { resultSet => + while (resultSet.next()) { + val key = implicitly[RowParser[Key]].parse(result) + val value = implicitly[RowParser[Value]].parse(result) - mm.get(key) - .map { foundValue => - mm += (key -> (foundValue + value)) - } - .getOrElse { - mm += (key -> Set(value)) + mm.updateWith(key) { vOpt => + Some(vOpt.getOrElse(Set.newBuilder).addOne(value)) } + } } + mm.view.mapValues(_.result()).toMap } - mm.toMap - } + + // Allow implicitly converting + implicit def funcAsRowParser[A](parser: (SqlRow) => A): RowParser[A] = apply(parser) + } diff --git a/relate/src/main/scala/com/lucidchart/relate/SqlQuery.scala b/relate/src/main/scala/com/lucidchart/relate/SqlQuery.scala index 5e744c0..6fba88d 100644 --- a/relate/src/main/scala/com/lucidchart/relate/SqlQuery.scala +++ b/relate/src/main/scala/com/lucidchart/relate/SqlQuery.scala @@ -28,7 +28,7 @@ trait Sql extends CollectionsSql { self => protected val parsedQuery: String - protected def applyParams(stmt: PreparedStatement) + protected def applyParams(stmt: PreparedStatement): Unit protected[relate] class BaseStatement(val connection: Connection) { protected val parsedQuery = self.parsedQuery @@ -150,8 +150,8 @@ trait Sql extends CollectionsSql { * @return * the auto-incremented key */ - def executeInsertSingle[U](parser: SqlRow => U)(implicit connection: Connection): U = - insertionStatement.execute(_.asSingle(parser)) + def executeInsertSingle[U](parser: RowParser[U])(implicit connection: Connection): U = + insertionStatement.execute(_.asSingle[U](parser)) def as[A: RowParser]()(implicit connection: Connection): A = normalStatement.execute(_.as[A]) @@ -165,7 +165,7 @@ trait Sql extends CollectionsSql { * the results as a single record */ def asSingle[A](parser: SqlRow => A)(implicit connection: Connection): A = normalStatement.execute(_.asSingle(parser)) - def asSingle[A: RowParser]()(implicit connection: Connection): A = normalStatement.execute(_.asSingle[A]) + def asSingle[A: RowParser](implicit connection: Connection): A = normalStatement.execute(_.asSingle[A]) /** * Execute this query and get back the result as an optional single record @@ -178,7 +178,7 @@ trait Sql extends CollectionsSql { */ def asSingleOption[A](parser: SqlRow => A)(implicit connection: Connection): Option[A] = normalStatement.execute(_.asSingleOption(parser)) - def asSingleOption[A: RowParser]()(implicit connection: Connection): Option[A] = + def asSingleOption[A: RowParser](implicit connection: Connection): Option[A] = normalStatement.execute(_.asSingleOption[A]) /** @@ -249,7 +249,7 @@ trait Sql extends CollectionsSql { def asMultiMap[U, V](parser: SqlRow => (U, V))(implicit connection: Connection): Map[U, Set[V]] = normalStatement.execute(_.asMultiMap(parser)) - def asMultiMap[U, V]()(implicit connection: Connection, p: RowParser[(U, V)]): Map[U, Set[V]] = + def asMultiMap[U, V](implicit connection: Connection, p: RowParser[(U, V)]): Map[U, Set[V]] = normalStatement.execute(_.asMultiMap[U, V]) /** @@ -260,7 +260,7 @@ trait Sql extends CollectionsSql { * @return * the results as a single value */ - def asScalar[A]()(implicit connection: Connection): A = normalStatement.execute(_.asScalar[A]()) + def asScalar[A](implicit connection: Connection): A = normalStatement.execute(_.asScalar[A]) /** * Execute this query and get back the result as an optional single value. Assumes that there is only one row and one @@ -272,7 +272,7 @@ trait Sql extends CollectionsSql { * @return * the results as an optional single value */ - def asScalarOption[A]()(implicit connection: Connection): Option[A] = normalStatement.execute(_.asScalarOption[A]()) + def asScalarOption[A](implicit connection: Connection): Option[A] = normalStatement.execute(_.asScalarOption[A]) /** * The asIterator method returns an Iterator that will stream data out of the database. This avoids an diff --git a/relate/src/main/scala/com/lucidchart/relate/SqlResult.scala b/relate/src/main/scala/com/lucidchart/relate/SqlResult.scala index 9b1014e..970beeb 100644 --- a/relate/src/main/scala/com/lucidchart/relate/SqlResult.scala +++ b/relate/src/main/scala/com/lucidchart/relate/SqlResult.scala @@ -21,36 +21,30 @@ object SqlResult { */ class SqlResult(val resultSet: java.sql.ResultSet) extends ResultSetWrapper with CollectionsSqlResult { - def as[A: RowParser](): A = implicitly[RowParser[A]].parse(asRow) + def as[A: RowParser]: A = implicitly[RowParser[A]].parse(asRow) - def asSingle[A: RowParser](): A = asCollection[A, Seq](1).head - def asSingle[A](parser: SqlRow => A): A = asCollection[A, Seq](parser, 1).head - def asSingleOption[A: RowParser](): Option[A] = asCollection[A, Seq](1).headOption - def asSingleOption[A](parser: SqlRow => A): Option[A] = asCollection[A, Seq](parser, 1).headOption - def asSet[A: RowParser](): Set[A] = asCollection[A, Set](Long.MaxValue) - def asSet[A](parser: SqlRow => A): Set[A] = asCollection[A, Set](parser, Long.MaxValue) - def asSeq[A: RowParser](): Seq[A] = asCollection[A, Seq](Long.MaxValue) - def asSeq[A](parser: SqlRow => A): Seq[A] = asCollection[A, Seq](parser, Long.MaxValue) - def asIterable[A: RowParser](): Iterable[A] = asCollection[A, Iterable](Long.MaxValue) - def asIterable[A](parser: SqlRow => A): Iterable[A] = asCollection[A, Iterable](parser, Long.MaxValue) - def asList[A: RowParser](): List[A] = asCollection[A, List](Long.MaxValue) - def asList[A](parser: SqlRow => A): List[A] = asCollection[A, List](parser, Long.MaxValue) - def asMap[U, V]()(implicit p: RowParser[(U, V)]): Map[U, V] = asPairCollection[U, V, Map](Long.MaxValue) - def asMap[U, V](parser: SqlRow => (U, V)): Map[U, V] = asPairCollection[U, V, Map](parser, Long.MaxValue) - def asMultiMap[U, V]()(implicit p: RowParser[(U, V)]): Map[U, Set[V]] = asMultiMap(p.parse) - def asMultiMap[U, V](parser: SqlRow => (U, V)): Map[U, Set[V]] = { - val mm: mutable.MultiMap[U, V] = new mutable.HashMap[U, mutable.Set[V]] with mutable.MultiMap[U, V] + def asSingle[A: RowParser]: A = asCollection[A, Seq](1).head + def asSingleOption[A: RowParser]: Option[A] = asCollection[A, Seq](1).headOption + def asSet[A: RowParser]: Set[A] = asCollection[A, Set](Long.MaxValue) + def asSeq[A: RowParser]: Seq[A] = asCollection[A, Seq](Long.MaxValue) + def asIterable[A: RowParser]: Iterable[A] = asCollection[A, Iterable](Long.MaxValue) + def asList[A: RowParser]: List[A] = asCollection[A, List](Long.MaxValue) + def asMap[U, V](implicit p: RowParser[(U, V)]): Map[U, V] = asPairCollection[U, V, Map](Long.MaxValue) + def asMultiMap[U, V](implicit p: RowParser[(U, V)]): Map[U, Set[V]] = { + val mm = new mutable.HashMap[U, mutable.Builder[V, Set[V]]] withResultSet { resultSet => while (resultSet.next()) { - val parsed = parser(asRow) - mm.addBinding(parsed._1, parsed._2) + val (key, value) = p.parse(asRow) + mm.updateWith(key) { vOpt => + Some(vOpt.getOrElse(Set.newBuilder).addOne(value)) + } } } - mm.toMap.map(x => x._1 -> x._2.toSet) + mm.view.mapValues(_.result()).toMap } - def asScalar[A](): A = asScalarOption.get - def asScalarOption[A](): Option[A] = { + def asScalar[A]: A = asScalarOption.get + def asScalarOption[A]: Option[A] = { if (resultSet.next()) { Some(resultSet.getObject(1).asInstanceOf[A]) } else { diff --git a/relate/src/main/scala/com/lucidchart/relate/SqlRow.scala b/relate/src/main/scala/com/lucidchart/relate/SqlRow.scala index 1cf7bf4..076ef10 100644 --- a/relate/src/main/scala/com/lucidchart/relate/SqlRow.scala +++ b/relate/src/main/scala/com/lucidchart/relate/SqlRow.scala @@ -6,7 +6,7 @@ import java.nio.ByteBuffer import java.sql.{Blob, Clob, NClob, Ref, RowId, SQLXML, Time, Timestamp} import java.time.{Instant, LocalDate} import java.util.{Calendar, UUID} -import scala.collection.JavaConverters._ +import scala.jdk.CollectionConverters._ import scala.util.Try object SqlRow { @@ -219,11 +219,13 @@ class SqlRow(val resultSet: java.sql.ResultSet) extends ResultSetWrapper { } } - def enum(column: String, e: Enumeration) = enumOption(column, e).get + def `enum`(column: String, e: Enumeration) = enumOption(column, e).get def enumOption(column: String, e: Enumeration): Option[e.Value] = for { id <- intOption(column) value <- Try(e(id)).toOption } yield value + // alternative name that doesn't conflict with scala 3 keyword + def enumeration(column: String, e: Enumeration) = `enum`(column, e) protected def getResultSetOption[A](f: => A): Option[A] = { f match { @@ -351,6 +353,6 @@ object SqlResultTypes { def uuidOption(column: String)(implicit sr: SqlRow) = sr.uuidOption(column) def uuidFromString(column: String)(implicit sr: SqlRow) = sr.uuidFromString(column) def uuidFromStringOption(column: String)(implicit sr: SqlRow) = sr.uuidFromStringOption(column) - def enum(column: String, e: Enumeration)(implicit sr: SqlRow) = sr.enum(column, e) + def `enum`(column: String, e: Enumeration)(implicit sr: SqlRow) = sr.`enum`(column, e) def enumOption(column: String, e: Enumeration)(implicit sr: SqlRow) = sr.enumOption(column, e) } diff --git a/relate/src/main/scala/com/lucidchart/relate/StatementPreparer.scala b/relate/src/main/scala/com/lucidchart/relate/StatementPreparer.scala index 50023aa..ce84f89 100644 --- a/relate/src/main/scala/com/lucidchart/relate/StatementPreparer.scala +++ b/relate/src/main/scala/com/lucidchart/relate/StatementPreparer.scala @@ -60,7 +60,7 @@ private[relate] sealed trait StatementPreparer { } private[relate] trait BaseStatementPreparer extends StatementPreparer { - protected def applyParams(stmt: PreparedStatement) + protected def applyParams(stmt: PreparedStatement): Unit protected def parsedQuery: String protected def timeout: Option[Int] = None diff --git a/relate/src/test/scala/ColReaderTest.scala b/relate/src/test/scala/ColReaderTest.scala index f689dc4..56aeb4a 100644 --- a/relate/src/test/scala/ColReaderTest.scala +++ b/relate/src/test/scala/ColReaderTest.scala @@ -34,7 +34,7 @@ class MockableRow extends SqlRow(null) { } object RecordA extends Mockito { - implicit val reader = new RowParser[RecordA] { + implicit val reader: RowParser[RecordA] = new RowParser[RecordA] { def parse(row: SqlRow): RecordA = { RecordA( row[BigDecimal]("bd"), @@ -103,7 +103,7 @@ case class RecordB( ) object RecordB extends Mockito { - implicit val reader = new RowParser[RecordB] { + implicit val reader: RowParser[RecordB] = new RowParser[RecordB] { def parse(row: SqlRow): RecordB = { RecordB( row.opt[BigDecimal]("bd"), diff --git a/relate/src/test/scala/ImplicitParsingTest.scala b/relate/src/test/scala/ImplicitParsingTest.scala index c9f9a23..a4546b6 100644 --- a/relate/src/test/scala/ImplicitParsingTest.scala +++ b/relate/src/test/scala/ImplicitParsingTest.scala @@ -15,7 +15,7 @@ class ImplicitParsingTest extends Specification with Mockito { case class TestRecord(name: String) object TestRecord { - implicit val praser = new RowParser[TestRecord] { + implicit val praser: RowParser[TestRecord] = new RowParser[TestRecord] { def parse(result: SqlRow): TestRecord = { TestRecord(result.string("name")) } @@ -25,7 +25,7 @@ class ImplicitParsingTest extends Specification with Mockito { case class TestKey(key: String) object TestKey { - implicit val parse = new RowParser[TestKey] { + implicit val parse: RowParser[TestKey] = new RowParser[TestKey] { def parse(result: SqlRow): TestKey = { TestKey(result.string("key")) } diff --git a/relate/src/test/scala/RelateITSpec.scala b/relate/src/test/scala/RelateITSpec.scala index af06e21..2532f71 100644 --- a/relate/src/test/scala/RelateITSpec.scala +++ b/relate/src/test/scala/RelateITSpec.scala @@ -136,7 +136,7 @@ trait Db { class RelateITSpec extends Specification with Db { - override def map(tests: => Fragments) = step(createDb) ^ tests ^ step(deleteDb) + override def map(tests: => Fragments) = step(createDb()) ^ tests ^ step(deleteDb()) def streamConnection = DriverManager.getConnection(url, props) def streamConnection2 = DriverManager.getConnection(url, props) @@ -237,7 +237,7 @@ class RelateITSpec extends Specification with Db { val id = sql""" INSERT INTO pokedex (name, description) VALUES ('Charmander', 'as adorable as a fire lizard can be') - """.executeInsertLong + """.executeInsertLong() true } @@ -459,7 +459,7 @@ class RelateITSpec extends Specification with Db { def insertOnSelect = sql""" SELECT id, name, description FROM pokedex - """.executeInsertLong + """.executeInsertLong() insertOnSelect must throwA[SQLException] } diff --git a/relate/src/test/scala/SqlResultSpec.scala b/relate/src/test/scala/SqlResultSpec.scala index 7b7f9ce..d9e4f6a 100644 --- a/relate/src/test/scala/SqlResultSpec.scala +++ b/relate/src/test/scala/SqlResultSpec.scala @@ -8,7 +8,7 @@ import java.time.Instant import java.util.{Calendar, Date, UUID} import org.specs2.mock.Mockito import org.specs2.mutable._ -import scala.collection.JavaConverters._ +import scala.jdk.CollectionConverters._ case class TestRecord( id: Long, @@ -16,7 +16,7 @@ case class TestRecord( ) object TestRecord { - implicit val TestRecordRowParser = new RowParser[TestRecord] { + implicit val TestRecordRowParser: RowParser[TestRecord] = new RowParser[TestRecord] { def parse(row: SqlRow): TestRecord = TestRecord( row.long("id"), row.string("name") @@ -303,7 +303,7 @@ class SqlResultSpec extends Specification with Mockito { val (rs, row, _) = getMocks rs.getRow() returns 3 - row.getRow mustEqual 3 + row.getRow() mustEqual 3 } } @@ -312,7 +312,7 @@ class SqlResultSpec extends Specification with Mockito { val (rs, _, result) = getMocks rs.wasNull() returns true - result.wasNull mustEqual true + result.wasNull() mustEqual true } }