Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ jobs:
- name: Submit Dependencies
uses: scalacenter/sbt-dependency-submission@v2
with:
modules-ignore: ldbcjs_3 ldbcjs_3 otel_3 mcp-ldbc-document-server_sjs1_3 docs_3 docs_3 zio_3 ldbcnative_3 ldbcnative_3 ldbcjvm_3 ldbcjvm_3 hikaricp_3 tests_sjs1_3 tests_sjs1_3 http4s_3 aws-iam-auth_3 tests_3 tests_3 benchmark_3 benchmark_3 tests_native0.4_3 tests_native0.4_3
modules-ignore: ldbcjs_3 ldbcjs_3 otel_3 mcp-ldbc-document-server_sjs1_3 docs_3 docs_3 zio_3 ldbcnative_3 ldbcnative_3 ldbcjvm_3 ldbcjvm_3 hikaricp_3 tests_sjs1_3 tests_sjs1_3 http4s_3 aws-iam-auth_3 tests_3 tests_3 benchmark_3 benchmark_3 tests_native0.5_3 tests_native0.5_3
configs-ignore: test scala-tool scala-doc-tool test-internal

validate-steward:
Expand Down
56 changes: 29 additions & 27 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@ ThisBuild / mimaBinaryIssueFilters ++= List(
"ldbc.connector.net.packet.response.BinaryProtocolResultSetRowPacket.decodeValue"
)
)
// for Otel4s
ThisBuild / resolvers += Resolver.sonatypeCentralSnapshots

lazy val sql = crossProject(JVMPlatform, JSPlatform, NativePlatform)
.crossType(CrossType.Pure)
.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"
)
)

Expand All @@ -57,8 +59,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)
Expand All @@ -68,9 +70,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.2",
"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)
Expand Down Expand Up @@ -106,10 +108,10 @@ 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",
"io.circe" %%% "circe-core" % "0.14.8",
"org.virtuslab" %%% "scala-yaml" % "0.0.7",
"org.typelevel" %%% "munit-cats-effect" % "2.1.0" % Test
"org.scala-lang.modules" %%% "scala-parser-combinators" % "2.4.0",
"io.circe" %%% "circe-core" % "0.14.15",
"org.virtuslab" %%% "scala-yaml" % "0.3.1",
"org.typelevel" %%% "munit-cats-effect" % "2.2.0-RC1" % Test
)
)
.jvmSettings(
Expand All @@ -136,8 +138,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(
Expand All @@ -152,15 +154,15 @@ 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.15.2",
"org.typelevel" %%% "otel4s-core-metrics" % "0.15.2",
"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.16.0-M1",
"org.typelevel" %%% "otel4s-core-metrics" % "0.16.0-M1",
"org.typelevel" %%% "twiddles-core" % "0.9.0",
"org.typelevel" %%% "munit-cats-effect" % "2.2.0-RC1" % Test
)
)
.jsSettings(
Expand All @@ -175,10 +177,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(
Expand Down Expand Up @@ -222,7 +224,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 {
Expand Down Expand Up @@ -302,7 +304,7 @@ lazy val otelExample = crossProject(JVMPlatform)
.example("otel", "OpenTelemetry example project")
.settings(
libraryDependencies ++= Seq(
"org.typelevel" %% "otel4s-oteljava" % "0.15.2",
"org.typelevel" %% "otel4s-oteljava" % "0.16.0-M1",
"io.opentelemetry" % "opentelemetry-exporter-otlp" % "1.60.1" % Runtime,
"io.opentelemetry" % "opentelemetry-sdk-extension-autoconfigure" % "1.60.1" % Runtime
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import javax.net.ssl.SSLContext

import cats.effect.*

import fs2.io.file.Path
import fs2.io.net.Network

class SSLPlatformTest extends FTestPlatform:
Expand All @@ -34,7 +35,7 @@ class SSLPlatformTest extends FTestPlatform:
test("SSL.fromKeyStoreFile") {
val classLoader = getClass.getClassLoader
val resource = classLoader.getResource("keystore.jks")
val keyStorePath = Paths.get(resource.toURI)
val keyStorePath = Path.fromNioPath(Paths.get(resource.toURI))
val storePassword = "password".toCharArray
val keyPassword = "password".toCharArray

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ object Connection:

for
sslOp <- ssl.toSSLNegotiationOptions(if debug then logger.some else none)
connection <- fromSocketGroup(
connection <- fromNetwork(
Network[F],
host,
port,
Expand Down Expand Up @@ -272,6 +272,64 @@ 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,
maxAllowedPacket: Int = MySQLConfig.DEFAULT_PACKET_SIZE,
databaseTerm: Option[DatabaseMetaData.DatabaseTerm] = None,
defaultAuthenticationPlugin: Option[AuthenticationPlugin[F]],
plugins: List[AuthenticationPlugin[F]],
telemetryConfig: TelemetryConfig = TelemetryConfig.default,
databaseMetrics: Option[DatabaseMetrics[F]] = None,
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,
maxAllowedPacket,
databaseTerm,
defaultAuthenticationPlugin,
plugins,
telemetryConfig,
databaseMetrics,
acquire,
release
)

@deprecated("0.X.0", "Use fromNetwork instead")
def fromSocketGroup[F[_]: Tracer: Console: Hashing: UUIDGen, A](
socketGroup: SocketGroup[F],
host: String,
Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ addSbtPlugin("pl.project13.scala" % "sbt-jmh"
addSbtPlugin("org.typelevel" % "sbt-typelevel" % "0.8.5")
addSbtPlugin("org.typelevel" % "sbt-typelevel-site" % "0.8.5")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.20.2")
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.17")
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.10")
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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading