diff --git a/build.sbt b/build.sbt index cda5f4b30..c7d21cb24 100644 --- a/build.sbt +++ b/build.sbt @@ -36,7 +36,7 @@ lazy val sql = crossProject(JVMPlatform, JSPlatform, NativePlatform) .module("sql", "JDBC API wrapped project with Effect System") .platformsSettings(JSPlatform, NativePlatform)( libraryDependencies ++= Seq( - "io.github.cquiroz" %%% "scala-java-time" % "2.5.0" + "io.github.cquiroz" %%% "scala-java-time" % "2.6.0" ) ) @@ -45,8 +45,8 @@ lazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform) .module("core", "Core project for ldbc") .settings( libraryDependencies ++= Seq( - "org.typelevel" %%% "cats-free" % "2.10.0", - "org.typelevel" %%% "cats-effect" % "3.6.2" + "org.typelevel" %%% "cats-free" % "2.13.0", + "org.typelevel" %%% "cats-effect" % "3.7.0-RC1" ) ) .dependsOn(sql) @@ -56,9 +56,9 @@ lazy val dsl = crossProject(JVMPlatform, JSPlatform, NativePlatform) .module("dsl", "Projects that provide a way to connect to the database") .settings( libraryDependencies ++= Seq( - "org.typelevel" %%% "twiddles-core" % "0.8.0", - "co.fs2" %%% "fs2-core" % "3.12.0", - "org.typelevel" %%% "munit-cats-effect" % "2.1.0" % Test + "org.typelevel" %%% "twiddles-core" % "0.9.0", + "co.fs2" %%% "fs2-core" % "3.13.0-M7", + "org.typelevel" %%% "munit-cats-effect" % "2.2.0-RC1" % Test ) ) .dependsOn(core) @@ -94,8 +94,8 @@ lazy val codegen = crossProject(JVMPlatform, JSPlatform, NativePlatform) .module("codegen", "Project to generate code from Sql") .settings( libraryDependencies ++= Seq( - "org.scala-lang.modules" %%% "scala-parser-combinators" % "2.3.0", - "org.typelevel" %%% "munit-cats-effect" % "2.1.0" % Test + "org.scala-lang.modules" %%% "scala-parser-combinators" % "2.4.0", + "org.typelevel" %%% "munit-cats-effect" % "2.2.0-RC1" % Test ) ) .jvmSettings( @@ -105,7 +105,7 @@ lazy val codegen = crossProject(JVMPlatform, JSPlatform, NativePlatform) ) ) .platformsSettings(JSPlatform, NativePlatform)( - libraryDependencies += "com.armanbilge" %%% "circe-scala-yaml" % "0.0.4" + libraryDependencies += "com.armanbilge" %%% "circe-scala-yaml" % "0.0.4-56-02c055c-20251223T140541Z-SNAPSHOT" ) .dependsOn(schema) @@ -125,8 +125,8 @@ lazy val authenticationPlugin = crossProject(JVMPlatform, JSPlatform, NativePlat .module("authentication-plugin", "MySQL authentication plugin written in pure Scala3") .settings( libraryDependencies ++= Seq( - "org.typelevel" %%% "cats-core" % "2.10.0", - "org.scodec" %%% "scodec-bits" % "1.1.38" + "org.typelevel" %%% "cats-core" % "2.12.0", + "org.scodec" %%% "scodec-bits" % "1.2.4" ) ) .jsSettings( @@ -141,14 +141,14 @@ lazy val connector = crossProject(JVMPlatform, JSPlatform, NativePlatform) .settings( scalacOptions += "-Ykind-projector:underscores", libraryDependencies ++= Seq( - "co.fs2" %%% "fs2-core" % "3.12.2", - "co.fs2" %%% "fs2-io" % "3.12.2", - "org.scodec" %%% "scodec-bits" % "1.1.38", - "org.scodec" %%% "scodec-core" % "2.2.2", - "org.scodec" %%% "scodec-cats" % "1.2.0", - "org.typelevel" %%% "otel4s-core-trace" % "0.14.0", - "org.typelevel" %%% "twiddles-core" % "0.8.0", - "org.typelevel" %%% "munit-cats-effect" % "2.1.0" % Test + "co.fs2" %%% "fs2-core" % "3.13.0-M7", + "co.fs2" %%% "fs2-io" % "3.13.0-M7", + "org.scodec" %%% "scodec-bits" % "1.2.4", + "org.scodec" %%% "scodec-core" % "2.3.1", + "org.scodec" %%% "scodec-cats" % "1.3.0-RC1", + "org.typelevel" %%% "otel4s-core-trace" % "0.15.0", + "org.typelevel" %%% "twiddles-core" % "0.9.0", + "org.typelevel" %%% "munit-cats-effect" % "2.2.0-RC1" % Test ) ) .jsSettings( @@ -163,10 +163,10 @@ lazy val awsAuthenticationPlugin = crossProject(JVMPlatform, JSPlatform, NativeP .module("aws-authentication-plugin", "Project for the plugin used with Aurora IAM authentication") .settings( libraryDependencies ++= Seq( - "co.fs2" %%% "fs2-core" % "3.12.2", - "co.fs2" %%% "fs2-io" % "3.12.2", - "io.github.cquiroz" %%% "scala-java-time" % "2.5.0", - "org.typelevel" %%% "munit-cats-effect" % "2.1.0" % Test + "co.fs2" %%% "fs2-core" % "3.13.0-M7", + "co.fs2" %%% "fs2-io" % "3.13.0-M7", + "io.github.cquiroz" %%% "scala-java-time" % "2.6.0", + "org.typelevel" %%% "munit-cats-effect" % "2.2.0-RC1" % Test ) ) .jsSettings( @@ -210,7 +210,7 @@ lazy val tests = crossProject(JVMPlatform, JSPlatform, NativePlatform) crossScalaVersions := Seq(scala3, scala37), name := "tests", description := "Projects for testing", - libraryDependencies += "org.typelevel" %%% "munit-cats-effect" % "2.1.0", + libraryDependencies += "org.typelevel" %%% "munit-cats-effect" % "2.2.0-RC1", Test / unmanagedSourceDirectories ++= { val sourceDir = (Test / sourceDirectory).value CrossVersion.partialVersion(scalaVersion.value) match { @@ -290,7 +290,7 @@ lazy val otelExample = crossProject(JVMPlatform) .example("otel", "OpenTelemetry example project") .settings( libraryDependencies ++= Seq( - "org.typelevel" %% "otel4s-oteljava" % "0.14.0", + "org.typelevel" %% "otel4s-oteljava" % "0.15.0", "io.opentelemetry" % "opentelemetry-exporter-otlp" % "1.57.0" % Runtime, "io.opentelemetry" % "opentelemetry-sdk-extension-autoconfigure" % "1.57.0" % Runtime ) diff --git a/module/ldbc-authentication-plugin/native/src/main/scala/ldbc/authentication/plugin/EncryptPasswordPlugin.scala b/module/ldbc-authentication-plugin/native/src/main/scala/ldbc/authentication/plugin/EncryptPasswordPlugin.scala index 65b42b368..da170804a 100644 --- a/module/ldbc-authentication-plugin/native/src/main/scala/ldbc/authentication/plugin/EncryptPasswordPlugin.scala +++ b/module/ldbc-authentication-plugin/native/src/main/scala/ldbc/authentication/plugin/EncryptPasswordPlugin.scala @@ -30,7 +30,7 @@ trait EncryptPasswordPlugin: (0 until length).map(pos => (from(pos) ^ scramble(pos % scrambleLength)).toByte).toArray private def encryptWithRSAPublicKey(input: Array[Byte], publicKey: String): Array[Byte] = - Zone { implicit zone => + Zone.acquire { implicit zone => val publicKeyCStr = toCString(publicKey) val bio = BIO_new_mem_buf(publicKeyCStr, publicKey.length) if bio == null then throw new RuntimeException("Failed to create a new memory BIO.") @@ -54,17 +54,17 @@ trait EncryptPasswordPlugin: throw new RuntimeException("Failed to set MGF1 hash function.") } - val inputBuf = alloc[UByte](input.length.toULong) + val inputBuf = alloc[UByte](input.length) for i <- input.indices do !(inputBuf + i) = input(i).toUByte val outLen = stackalloc[CSize]() - !outLen = 0.toULong + !outLen = 0.toCSize - if EVP_PKEY_encrypt(ctx, null, outLen, inputBuf, input.length.toULong) <= 0 then + if EVP_PKEY_encrypt(ctx, null, outLen, inputBuf, input.length.toCSize) <= 0 then throw new RuntimeException("Failed to obtain the output buffer size required for encryption.") val encryptedBuf = alloc[UByte](!outLen) - if EVP_PKEY_encrypt(ctx, encryptedBuf, outLen, inputBuf, input.length.toULong) <= 0 then + if EVP_PKEY_encrypt(ctx, encryptedBuf, outLen, inputBuf, input.length.toCSize) <= 0 then throw new RuntimeException("Encryption failed.") val result = Array.fill[Byte]((!outLen).toInt)(0) diff --git a/module/ldbc-aws-authentication-plugin/js/src/main/scala/ldbc/amazon/client/SimpleHttpClient.scala b/module/ldbc-aws-authentication-plugin/js/src/main/scala/ldbc/amazon/client/SimpleHttpClient.scala index df1c98246..d28f432c0 100644 --- a/module/ldbc-aws-authentication-plugin/js/src/main/scala/ldbc/amazon/client/SimpleHttpClient.scala +++ b/module/ldbc-aws-authentication-plugin/js/src/main/scala/ldbc/amazon/client/SimpleHttpClient.scala @@ -35,11 +35,11 @@ final case class SimpleHttpClient[F[_]: Network: Async]( override def createSocket(address: SocketAddress[Host], isSecure: Boolean, host: String): Resource[F, Socket[F]] = if isSecure then for - socket <- Network[F].client(address) + socket <- Network[F].connect(address) tlsContext <- Network[F].tlsContext.systemResource tlsSocket <- tlsContext .clientBuilder(socket) .withParameters(TLSParameters(servername = Some(host))) .build yield tlsSocket - else Network[F].client(address) + else Network[F].connect(address) diff --git a/module/ldbc-aws-authentication-plugin/jvm/src/main/scala/ldbc/amazon/client/SimpleHttpClient.scala b/module/ldbc-aws-authentication-plugin/jvm/src/main/scala/ldbc/amazon/client/SimpleHttpClient.scala index 0ffe3efd7..671acaeee 100644 --- a/module/ldbc-aws-authentication-plugin/jvm/src/main/scala/ldbc/amazon/client/SimpleHttpClient.scala +++ b/module/ldbc-aws-authentication-plugin/jvm/src/main/scala/ldbc/amazon/client/SimpleHttpClient.scala @@ -37,11 +37,11 @@ final case class SimpleHttpClient[F[_]: Network: Async]( override def createSocket(address: SocketAddress[Host], isSecure: Boolean, host: String): Resource[F, Socket[F]] = if isSecure then for - socket <- Network[F].client(address) + socket <- Network[F].connect(address) tlsContext <- Network[F].tlsContext.systemResource tlsSocket <- tlsContext .clientBuilder(socket) .withParameters(TLSParameters(serverNames = Some(List(new SNIHostName(host))))) .build yield tlsSocket - else Network[F].client(address) + else Network[F].connect(address) diff --git a/module/ldbc-aws-authentication-plugin/native/src/main/scala/ldbc/amazon/client/SimpleHttpClient.scala b/module/ldbc-aws-authentication-plugin/native/src/main/scala/ldbc/amazon/client/SimpleHttpClient.scala index 476c732d3..803944472 100644 --- a/module/ldbc-aws-authentication-plugin/native/src/main/scala/ldbc/amazon/client/SimpleHttpClient.scala +++ b/module/ldbc-aws-authentication-plugin/native/src/main/scala/ldbc/amazon/client/SimpleHttpClient.scala @@ -35,11 +35,11 @@ final case class SimpleHttpClient[F[_]: Network: Async]( override def createSocket(address: SocketAddress[Host], isSecure: Boolean, host: String): Resource[F, Socket[F]] = if isSecure then for - socket <- Network[F].client(address) + socket <- Network[F].connect(address) tlsContext <- Network[F].tlsContext.systemResource tlsSocket <- tlsContext .clientBuilder(socket) .withParameters(TLSParameters(serverName = Some(host))) .build yield tlsSocket - else Network[F].client(address) + else Network[F].connect(address) diff --git a/module/ldbc-connector/jvm/src/main/scala/ldbc/connector/SSLPlatform.scala b/module/ldbc-connector/jvm/src/main/scala/ldbc/connector/SSLPlatform.scala index 5aac3bcde..c9c046875 100644 --- a/module/ldbc-connector/jvm/src/main/scala/ldbc/connector/SSLPlatform.scala +++ b/module/ldbc-connector/jvm/src/main/scala/ldbc/connector/SSLPlatform.scala @@ -6,7 +6,7 @@ package ldbc.connector -import java.nio.file.Path +import java.nio.file.Path as JPath import java.security.KeyStore import javax.net.ssl.SSLContext @@ -15,6 +15,7 @@ import cats.* import cats.effect.Resource +import fs2.io.file.Path import fs2.io.net.tls.TLSContext import fs2.io.net.Network @@ -26,6 +27,19 @@ private[ldbc] trait SSLPlatform: override def tlsContext[F[_]: Network](implicit ev: ApplicativeError[F, Throwable]): Resource[F, TLSContext[F]] = Resource.pure(Network[F].tlsContext.fromSSLContext(ctx)) + /** Creates an `SSL` from the specified key store file. */ + @deprecated("Use overload that takes an fs2.io.file.Path instead", "0.X.0") + def fromKeyStoreFile( + file: JPath, + storePassword: Array[Char], + keyPassword: Array[Char] + ): SSL = + new SSL: + override def tlsContext[F[_]: Network](implicit ev: ApplicativeError[F, Throwable]): Resource[F, TLSContext[F]] = + Resource.eval( + Network[F].tlsContext.fromKeyStoreFile(fs2.io.file.Path.fromNioPath(file), storePassword, keyPassword) + ) + /** Creates an `SSL` from the specified key store file. */ def fromKeyStoreFile( file: Path, diff --git a/module/ldbc-connector/shared/src/main/scala/ldbc/connector/Connection.scala b/module/ldbc-connector/shared/src/main/scala/ldbc/connector/Connection.scala index aca2f00fa..0c4991d13 100644 --- a/module/ldbc-connector/shared/src/main/scala/ldbc/connector/Connection.scala +++ b/module/ldbc-connector/shared/src/main/scala/ldbc/connector/Connection.scala @@ -162,7 +162,7 @@ object Connection: for sslOp <- ssl.toSSLNegotiationOptions(if debug then logger.some else none) - connection <- fromSocketGroup( + connection <- fromNetwork( Network[F], host, port, @@ -246,6 +246,58 @@ object Connection: _ <- Resource.make(acquire(connection))(v => release(v, connection)) yield connection + def fromNetwork[F[_]: Tracer: Console: Hashing: UUIDGen, A]( + network: Network[F], + host: String, + port: Int, + user: String, + password: Option[String] = None, + database: Option[String] = None, + debug: Boolean = false, + socketOptions: List[SocketOption], + sslOptions: Option[SSLNegotiation.Options[F]], + readTimeout: Duration = Duration.Inf, + allowPublicKeyRetrieval: Boolean = false, + useCursorFetch: Boolean = false, + useServerPrepStmts: Boolean = false, + databaseTerm: Option[DatabaseMetaData.DatabaseTerm] = None, + defaultAuthenticationPlugin: Option[AuthenticationPlugin[F]], + plugins: List[AuthenticationPlugin[F]], + acquire: Connection[F] => F[A], + release: (A, Connection[F]) => F[Unit] + )(using ev: Async[F]): Resource[F, LdbcConnection[F]] = + + def fail[B](msg: String): Resource[F, B] = + Resource.eval(ev.raiseError(new SQLClientInfoException(msg))) + + def sockets: Resource[F, Socket[F]] = + (Hostname.fromString(host), Port.fromInt(port)) match + case (Some(validHost), Some(validPort)) => + network.connect(SocketAddress(validHost, validPort), socketOptions) + case (None, _) => fail(s"""Hostname: "$host" is not syntactically valid.""") + case (_, None) => fail(s"Port: $port falls out of the allowed range.") + + fromSockets( + sockets, + host, + port, + user, + password, + database, + debug, + sslOptions, + readTimeout, + allowPublicKeyRetrieval, + useCursorFetch, + useServerPrepStmts, + databaseTerm, + defaultAuthenticationPlugin, + plugins, + acquire, + release + ) + + @deprecated("0.X.0", "Use fromNetwork instead") def fromSocketGroup[F[_]: Tracer: Console: Hashing: UUIDGen, A]( socketGroup: SocketGroup[F], host: String, diff --git a/project/plugins.sbt b/project/plugins.sbt index 0650242ce..4ea026f48 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -6,7 +6,7 @@ addSbtPlugin("pl.project13.scala" % "sbt-jmh" addSbtPlugin("org.typelevel" % "sbt-typelevel" % "0.8.3") addSbtPlugin("org.typelevel" % "sbt-typelevel-site" % "0.8.3") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.20.1") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.17") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.9") addSbtPlugin("com.armanbilge" % "sbt-scala-native-config-brew-github-actions" % "0.4.0") addSbtPlugin("com.github.sbt" % "sbt-boilerplate" % "0.8.0") addSbtPlugin("io.chrisdavenport" %% "sbt-npm-package" % "0.2.0") diff --git a/tests/jvm/src/test/scala/ldbc/tests/ConnectionPoolDslTest.scala b/tests/jvm/src/test/scala/ldbc/tests/ConnectionPoolDslTest.scala index 1f8c3c02a..eb7487b5e 100644 --- a/tests/jvm/src/test/scala/ldbc/tests/ConnectionPoolDslTest.scala +++ b/tests/jvm/src/test/scala/ldbc/tests/ConnectionPoolDslTest.scala @@ -110,8 +110,6 @@ trait ConnectionPoolDslTest extends CatsEffectSuite: .fromConfig[IO](config.setMinConnections(2).setMaxConnections(5)) .use { pool => // Execute multiple queries concurrently - // TODO: Scala Native 0.5.x will support multi-threading, so we will verify again at that time. - // See: https://github.com/takapi327/ldbc/issues/536 val queries = (1 to 5).toList.parTraverse { i => country.selectAll .limit(1) diff --git a/tests/native/src/test/scala/ldbc/tests/ConnectionPoolDslTest.scala b/tests/native/src/test/scala/ldbc/tests/ConnectionPoolDslTest.scala index ce11b2bca..a03450eea 100644 --- a/tests/native/src/test/scala/ldbc/tests/ConnectionPoolDslTest.scala +++ b/tests/native/src/test/scala/ldbc/tests/ConnectionPoolDslTest.scala @@ -112,7 +112,7 @@ trait ConnectionPoolDslTest extends CatsEffectSuite: .fromConfig[IO](config.setMinConnections(2).setMaxConnections(5)) .use { pool => // Execute multiple queries concurrently - val queries = (1 to 5).toList.traverse { i => + val queries = (1 to 5).toList.parTraverse { i => country.selectAll .limit(1) .offset(i)