ldbc v0.8.0 is released. 🎉
This release fixes a SQL injection in client-side prepared statements under the NO_BACKSLASH_ESCAPES sql_mode, adds the JDBC 4.3 enquote APIs, and makes ldbc-plugin usable from sbt 2.
Note
ldbc is pre-1.0 software and is still undergoing active development. New versions are not binary compatible with prior versions, although in most cases user code will be source compatible.
The major version will be the stable version.
Important
Upgrading is recommended if you use the ldbc connector. See 1. SQL injection under NO_BACKSLASH_ESCAPES fixed for details.
A full migration guide is available in English and Japanese.
What's Changed
1. SQL injection under NO_BACKSLASH_ESCAPES fixed
In 0.7.x and earlier, client-side prepared statements (useServerPrepStmts = false, the default) escaped string parameters with backslash escaping only (' -> \') and never consulted the server sql_mode.
When a session runs with the MySQL NO_BACKSLASH_ESCAPES sql_mode, a backslash is an ordinary character. \' therefore does not neutralize the quote, and a string parameter can break out of its literal.
// 0.7.x and earlier, in a session with sql_mode = 'NO_BACKSLASH_ESCAPES'
ps.setString(1, "zzz' OR 1=1 -- ")
// Rendered SQL: WHERE t.name = 'zzz\' OR 1=1 -- '
// => (name = 'zzz\') OR 1=1 ... always true0.8.0 fixes this in three parts.
1. All escaping is centralised in QueryRenderer
QueryRenderer is now the only path that turns a string parameter into a SQL literal. Parameter itself no longer exposes a SQL-text representation for strings, so a path that bypasses the sql_mode-aware logic cannot exist by construction.
2. Escaping follows the server sql_mode
| sql_mode | Escaping |
|---|---|
| default | ' -> \', " -> \", \ -> \\, control characters -> \0 \b \n \r \Z |
NO_BACKSLASH_ESCAPES |
' -> '' (doubling the single quote) |
Doubling the quote under NO_BACKSLASH_ESCAPES is the only way to embed a quote such that it can never be consumed by a preceding backslash.
3. The sql_mode is tracked for the life of the session
Protocol.noBackslashEscapes has been added. It is seeded from the status flags of the initial handshake and then updated from the status flags of every OK / EOF packet received. A SET SESSION sql_mode = ... issued after connecting is therefore reflected in subsequent query construction.
No user code changes are required.
2. JDBC 4.3 enquote API support
Following MySQL Connector/J 9.7.0 (WL #17215), four methods have been added to ldbc.sql.Statement. Use them to safely quote values and identifiers when assembling SQL dynamically.
| Method | Purpose |
|---|---|
enquoteLiteral(value) |
Wrap a string in single quotes as a literal |
enquoteIdentifier(identifier, alwaysQuote) |
Quote an identifier |
enquoteNCharLiteral(value) |
Produce an N-prefixed national character literal |
isSimpleIdentifier(identifier) |
Report whether an identifier can be used without quoting |
for
stmt <- conn.createStatement()
a <- stmt.enquoteLiteral("G'Day") // 'G''Day'
b <- stmt.enquoteIdentifier("my table", false) // `my table`
c <- stmt.enquoteIdentifier("user", true) // `user`
d <- stmt.enquoteNCharLiteral("Hello") // N'Hello'
e <- stmt.isSimpleIdentifier("user_name") // true
f <- stmt.isSimpleIdentifier("select") // false (reserved word)
yield ()Following the MySQL rules, isSimpleIdentifier treats an identifier as simple when it consists only of [0-9a-zA-Z$_] or extended characters (U+0080 and above), is not made up solely of digits, is at most 64 characters long, and is not a reserved word.
When the ANSI_QUOTES sql_mode is enabled, the identifier quote character is " rather than `.
The methods are available on both Statement and PreparedStatement, for the ldbc connector as well as the jdbc connector.
Note: the existing
ident()helper is for embedding identifiers inside thesqlinterpolator and remains available. UseenquoteIdentifierwhen you need compatibility with the standard JDBC API, or control overalwaysQuote.
3. ldbc-plugin now supports sbt 2
ldbc-plugin is cross-built for both sbt 1 and sbt 2. Artifacts for sbt 1 (Scala 2.12) and sbt 2 (Scala 3) are published side by side.
The declaration is identical for either version; sbt resolves the right artifact.
// project/plugins.sbt — the same for sbt 1.x and sbt 2.x
addSbtPlugin("io.github.takapi327" % "ldbc-plugin" % "0.8.0")This is the goal set out for the 0.8.x series in the roadmap. Note that the ldbc build itself still runs on sbt 1, because sbt-typelevel has not been published for sbt 2 yet.
4. Column-order bug in insert fixed
The tuple overload of insert now goes through the entity mapping defined by the table's * projection.
In 0.7.x the tuple was cast onto the column encoder directly, so values could be inserted into the wrong columns whenever the field order of the model differed from the column order of the * projection.
userTable.insert((1L, "Alice", Some(20)))If the * projection is not ordered as id *: name *: age, code like the above produces a different parameter order in 0.8.0 than in 0.7.x. The change makes the result correct, but verifying it with your tests after upgrading is recommended.
5. Dependency updates
| Library | Before (0.7.x) | After (0.8.0) |
|---|---|---|
| MySQL Connector/J | 9.6.0 | 9.7.0 |
| twiddles-core | 0.10.0 | 1.1.0 |
ldbc.connector.data.Constants.DRIVER_VERSION has been updated to 0.8.0 as well.
Breaking Changes
Parameter is now a sealed trait and sql has been removed
ldbc.connector.data.Parameter is now a sealed trait with one case class per type, and def sql: String has been removed.
Before (0.7.x):
trait Parameter:
def columnDataType: ColumnDataType
def sql: String
def encode: BitVectorAfter (0.8.0):
sealed trait Parameter:
def columnDataType: ColumnDataType
def encode: BitVectorThis is part of the SQL injection fix above. Rendering a string into a SQL literal depends on the sql_mode, so that representation was removed from Parameter to leave QueryRenderer as the only route.
- Custom
Parameterimplementations are no longer possible now that the trait issealed. Use the factory methods such asParameter.string(...) - Code that read
param.sqlshould useparam.toString. Note thattoStringis a sql_mode-independent literal intended for display and diagnostics, and must not be used to assemble SQL for execution
params removed from SQLException
The params: SortedMap[Int, Parameter] parameter has been removed from SQLException and its subclasses, as well as from the signature of ERRPacket.toException.
As a result, the following are no longer emitted:
- The OpenTelemetry attributes
error.parameter.$i.type/error.parameter.$i.value - The "and the arguments were" section of the exception message
This closes the paths by which bound values could leak through exception messages and telemetry. If you build dashboards or alerts on those attributes, you are affected.
Four abstract methods added to Statement
The four enquote methods are added as abstract members of ldbc.sql.Statement. There is no impact as long as you use the connectors that ldbc provides, but implementing Statement or PreparedStatement yourself will now fail to compile.
twiddles-core is now 1.1.0
The twiddles-core dependency of ldbc-dsl and ldbc-connector moved from 0.10.0 to 1.1.0. Align the version if your project uses twiddles directly.
What has not changed
The Java and Scala requirements are unchanged from 0.7.x.
| 0.7.x | 0.8.0 | |
|---|---|---|
| Java versions | 17, 21, 25 | unchanged |
| Scala versions | 3.3.x / 3.8.x | unchanged |
Deprecated APIs
The following APIs deprecated in 0.7.0 remain available in 0.8.0, but are scheduled for removal in a future release.
| API | Deprecated in | Replacement |
|---|---|---|
sc(identifier) |
0.7.0 | ident(identifier) |
Connection.fromSocketGroup(...) |
0.7.0 | Connection.fromNetwork(...) |
SSL.fromKeyStoreFile(java.nio.file.Path, ...) |
0.7.0 | SSL.fromKeyStoreFile(fs2.io.file.Path, ...) |
🚀 Features
- Feature/2026 06 support my sql 9.7.0 by @takapi327 in #774
💪 Enhancement
- Enhancement/2026 08 support sbt 2.x by @takapi327 in #836
🪲 Bug Fixes
- Dependencies/2026 07 update twiddles by @takapi327 in #799
🧰 Maintenance
- Chore/2026 07 upadate string escape by @takapi327 in #791
🔧 Refactoring
- Fixed aws simple http client test by @takapi327 in #811
- Refactor/2026 08 update licence by @takapi327 in #812
- Refactor/2026 08 delete parameter in exception by @takapi327 in #814
- Refactor/2026 08 change npm package by @takapi327 in #813
- Refactor/2026 08 delete scala native config brew plugin by @takapi327 in #838
⛓️ Dependency update
- Update opentelemetry-exporter-otlp, ... from 1.64.0 to 1.65.0 by @scala-steward in #805
- Update sbt, scripted-plugin from 1.12.14 to 1.12.15 by @scala-steward in #806
- Update otel4s-core-metrics, ... from 1.0.1 to 1.1.0 by @scala-steward in #807
- Update otel4s-sdk-testkit from 0.19.0 to 0.19.1 by @scala-steward in #808
- Update logback-classic from 1.6.1 to 1.6.3 by @scala-steward in #815
- Update zio-http from 3.11.3 to 3.11.4 by @scala-steward in #820
- Update sbt, scripted-plugin from 1.12.15 to 1.13.0 by @scala-steward in #821
Full Changelog: v0.7.1...v0.8.0