Skip to content

feat(sql): add zio-blocks-sql module — Schema-driven SQL database access#1224

Open
987Nabil wants to merge 25 commits intomainfrom
feat/sql
Open

feat(sql): add zio-blocks-sql module — Schema-driven SQL database access#1224
987Nabil wants to merge 25 commits intomainfrom
feat/sql

Conversation

@987Nabil
Copy link
Contributor

Summary

Adds a new zio-blocks-sql module providing schema-driven, type-safe SQL database access that integrates with zio-blocks' existing Schema, Scope, and optics infrastructure. Inspired by Magnum.

Closes #1216

Modules

Module Type Purpose
sql crossProject (JVM/JS) Core: DbCodec, sql"", DDL, Transactor, context functions
sql-zio JVM only ZIO effect wrappers (TransactorZIO)

Key Features

Schema-Driven DbCodec Derivation

case class User(id: Int, name: String, email: String)
object User { implicit val schema: Schema[User] = Schema.derived }

// Automatic codec derivation via Deriver[DbCodec]
val codec = Schema[User].deriving(DbCodecDeriver).derive
// columns: Vector("id", "name", "email")

Safe SQL Interpolation

import zio.blocks.sql.*

val name = "Alice"
val frag = sql"SELECT * FROM users WHERE name = $name"
frag.sql(SqlDialect.PostgreSQL) // "SELECT * FROM users WHERE name = $1"
frag.sql(SqlDialect.SQLite)     // "SELECT * FROM users WHERE name = ?"
// Values are NEVER interpolated into SQL text — always parameterized

Transactor with Context Functions

val transactor = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)

transactor.connect {
  val users = SqlOps.query[User](sql"SELECT * FROM users")
  // users: List[User]
}

transactor.transact {
  SqlOps.update(sql"INSERT INTO users VALUES (${1}, ${"Alice"}, ${"alice@example.com"})")
  // Auto-commit on success, rollback on exception
}

DDL Generation

val table = Table.derived[User](SqlDialect.PostgreSQL)
// table.name == "users" (auto-pluralized snake_case)
// table.createTable → CREATE TABLE IF NOT EXISTS users (...)
// table.dropTable   → DROP TABLE IF EXISTS users

ZIO Integration

val zioTransactor = TransactorZIO.fromUrl("jdbc:sqlite:test.db", SqlDialect.SQLite)

zioTransactor.transact {
  SqlOps.update(sql"INSERT INTO users VALUES (${1}, ${"Alice"}, ${"a@b.com"})")
}: Task[Int]

Architecture

sql/shared/   — Platform-agnostic core (DbCodec, Frag, SqlDialect, Transactor trait, etc.)
sql/jvm/      — JDBC implementation (JdbcTransactor, JdbcResultReader, JdbcParamWriter)
sql/js/       — Empty (abstract types only, no backend for v1)
sql-zio/      — ZIO wrappers (TransactorZIO)
  • Zero-dependency core: sql depends only on zio-blocks-schema + zio-blocks-scope
  • Direct-style: No effect types in core. DbCon ?=> A / DbTx ?=> A context functions
  • Scala 3 only: Uses given, extension, context functions

Dialect Support

Feature PostgreSQL SQLite
Type mapping
Param placeholder $N ?
DDL generation

Test Coverage

  • 194 tests across 9 test suites
  • 100% statement coverage, 100% branch coverage (scoverage)
  • SQLite in-memory integration tests with full INSERT→SELECT roundtrip

Non-Goals (explicitly out of scope)

  • JOINs, subqueries, aggregations, window functions
  • ORM features (lazy loading, change tracking, identity maps)
  • Migration system (separate: Schema Migration System for ZIO Schema 2 #519)
  • Batch operations, streaming
  • More than 2 SQL dialects for v1

Related

Copilot AI review requested due to automatic review settings March 14, 2026 13:11
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new zio-blocks-sql module with schema-driven SQL building/execution primitives (fragments, dialects, codecs, DDL, and JDBC/ZIO transactors) plus a broad test suite.

Changes:

  • Introduces core SQL abstractions (Frag, SqlDialect, DbValue, DbCodec + derivation, Table, Ddl, SqlOps) in sql/shared.
  • Adds a JDBC backend (JdbcTransactor, readers/writers, connection wrappers) in sql/jvm and ZIO wrappers in sql-zio.
  • Wires new modules into the build and adds extensive unit/integration tests.

Reviewed changes

Copilot reviewed 28 out of 33 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
sql/shared/src/main/scala/zio/blocks/sql/Transactor.scala Adds the core transactor interface using context functions.
sql/shared/src/main/scala/zio/blocks/sql/Table.scala Adds schema-derived table naming, pluralization, and basic DDL helpers.
sql/shared/src/main/scala/zio/blocks/sql/SqlOps.scala Adds query/update helpers over DbCon/DbTx.
sql/shared/src/main/scala/zio/blocks/sql/SqlNameMapper.scala Adds name mapping strategies (snake_case, identity, custom).
sql/shared/src/main/scala/zio/blocks/sql/SqlInterpolator.scala Adds sql"" interpolator plumbing and parameter typeclass.
sql/shared/src/main/scala/zio/blocks/sql/SqlDialect.scala Adds PostgreSQL/SQLite dialects (types + placeholders).
sql/shared/src/main/scala/zio/blocks/sql/Frag.scala Adds SQL fragment representation and dialect rendering.
sql/shared/src/main/scala/zio/blocks/sql/Ddl.scala Adds basic CREATE/DROP table DDL generation.
sql/shared/src/main/scala/zio/blocks/sql/DbValue.scala Adds a sum type for DB values (params + type mapping).
sql/shared/src/main/scala/zio/blocks/sql/DbTx.scala Adds a transaction marker context (DbTx).
sql/shared/src/main/scala/zio/blocks/sql/DbConnection.scala Adds backend-agnostic connection/statement/resultset interfaces.
sql/shared/src/main/scala/zio/blocks/sql/DbCon.scala Adds connection + dialect context type.
sql/shared/src/main/scala/zio/blocks/sql/DbCodecDeriver.scala Implements Schema-driven derivation of DbCodec.
sql/shared/src/main/scala/zio/blocks/sql/DbCodec.scala Defines DbCodec, result reading, and param writing interfaces.
sql/shared/src/test/scala/zio/blocks/sql/TableSpec.scala Tests for table naming/pluralization and DDL helpers.
sql/shared/src/test/scala/zio/blocks/sql/SqlNameMapperSpec.scala Tests naming mappers, especially snake_case rules.
sql/shared/src/test/scala/zio/blocks/sql/SqlInterpolatorSpec.scala Tests DbParam conversions and basic sql"" behavior.
sql/shared/src/test/scala/zio/blocks/sql/SqlDialectSpec.scala Tests dialect type mapping and placeholders.
sql/shared/src/test/scala/zio/blocks/sql/FragSpec.scala Tests fragment rendering, composition, and parameterization safety.
sql/shared/src/test/scala/zio/blocks/sql/DdlSpec.scala Tests CREATE/DROP DDL formatting and nullability.
sql/shared/src/test/scala/zio/blocks/sql/DbValueSpec.scala Tests DbValue constructors/extraction.
sql/shared/src/test/scala/zio/blocks/sql/DbCodecSpec.scala Tests codec derivation, options, rename/transient modifiers, mappers.
sql/jvm/src/main/scala/zio/blocks/sql/JdbcTransactor.scala Adds JDBC transactor implementation.
sql/jvm/src/main/scala/zio/blocks/sql/JdbcResultReader.scala Adds JDBC ResultSet adapter for DbResultReader.
sql/jvm/src/main/scala/zio/blocks/sql/JdbcParamWriter.scala Adds JDBC PreparedStatement adapter for DbParamWriter.
sql/jvm/src/main/scala/zio/blocks/sql/JdbcConnection.scala Adds JDBC connection/statement/resultset adapters.
sql/jvm/src/test/scala/zio/blocks/sql/TransactorSpec.scala Adds SQLite in-memory integration tests for queries/transactions/types.
sql-zio/src/main/scala/zio/blocks/sql/zio/TransactorZIO.scala Adds ZIO Task wrappers and layer helper for transactors.
build.sbt Adds sql/sql-zio modules and wires them into test/doc aliases.

Comment on lines +8 to +13
def createTable: Frag = {
val columnDefs = codec.columns.map { col =>
ColumnDef(col, dialect.typeName(DbValue.DbString("")), nullable = false)
}
Ddl.createTable(name, columnDefs)
}
Comment on lines +6 to +8
trait DbParam[A] {
def toDbValue(value: A): DbValue
}
Comment on lines +89 to +92
implicit class SqlStringContext(val sc: StringContext) extends AnyVal {
def sql(args: DbValue*): Frag =
Frag(sc.parts.toIndexedSeq, args.toIndexedSeq)
}
Comment on lines +89 to +92
implicit class SqlStringContext(val sc: StringContext) extends AnyVal {
def sql(args: DbValue*): Frag =
Frag(sc.parts.toIndexedSeq, args.toIndexedSeq)
}
Comment on lines +204 to +218
)(implicit F: HasBinding[F], D: HasInstance[F]): Lazy[DbCodec[A]] = Lazy {
if (isOptionType(typeId, cases)) {
val someCase = cases(1)
val someRecord = someCase.value.asRecord.get
val innerField = someRecord.fields(0)
val innerCodec = D.instance(innerField.value.metadata).force.asInstanceOf[DbCodec[Any]]

new DbCodec[A] {
val columns: IndexedSeq[String] = innerCodec.columns

def readValue(reader: DbResultReader, startIndex: Int): A = {
val innerValue = innerCodec.readValue(reader, startIndex)
val result: Any = if (reader.wasNull) None else Some(innerValue)
result.asInstanceOf[A]
}
private[sql] def pluralize(s: String): String =
if (s.isEmpty) s
else if (s.endsWith("s") || s.endsWith("x") || s.endsWith("ch") || s.endsWith("sh")) s + "es"
else if (s.endsWith("z")) s + "zes" // quiz -> quizzes, buzz -> buzzes
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure this is a good idea at all

…oc aliases

These aliases are used by CI across ALL Scala versions (2.13, 3.3, 3.7).
The sql module only supports Scala 3.3+ (crossScalaVersions does not include 2.13).
This matches how other Scala-3-only modules (scope-examples, schema-examples)
are also excluded from these aliases.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new zio-blocks-sql module (plus sql-zio wrapper) to provide schema-driven, type-safe SQL fragments, codec derivation, DDL helpers, and JDBC-backed transacting/connecting, integrated with zio-blocks-schema.

Changes:

  • Introduces core SQL types (Frag, DbValue, DbCodec, SqlDialect) plus interpolation and basic execution helpers (SqlOps).
  • Adds JDBC implementation (JdbcTransactor, JDBC readers/writers) and ZIO wrappers (TransactorZIO).
  • Adds a comprehensive test suite for naming, interpolation, dialect behavior, DDL generation, codec derivation, and JDBC roundtrips; wires new modules into build.sbt.

Reviewed changes

Copilot reviewed 28 out of 33 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
sql/shared/src/test/scala/zio/blocks/sql/TableSpec.scala Tests table-name derivation, pluralization, and DDL fragments from Table.
sql/shared/src/test/scala/zio/blocks/sql/SqlNameMapperSpec.scala Tests snake_case/identity/custom name mappers.
sql/shared/src/test/scala/zio/blocks/sql/SqlInterpolatorSpec.scala Tests DbParam conversions and sql"" parameter capture.
sql/shared/src/test/scala/zio/blocks/sql/SqlDialectSpec.scala Tests dialect type-name mapping and placeholder rendering.
sql/shared/src/test/scala/zio/blocks/sql/FragSpec.scala Tests Frag rendering, composition, and SQL injection avoidance via params.
sql/shared/src/test/scala/zio/blocks/sql/DdlSpec.scala Tests DDL generation formatting and nullability handling.
sql/shared/src/test/scala/zio/blocks/sql/DbValueSpec.scala Tests DbValue constructors/extractors.
sql/shared/src/test/scala/zio/blocks/sql/DbCodecSpec.scala Tests schema-driven DbCodec derivation and modifiers (rename/transient).
sql/shared/src/test/scala/zio/blocks/sql/.gitkeep Placeholder file for test directory.
sql/shared/src/main/scala/zio/blocks/sql/Transactor.scala Core direct-style transactor API using context functions.
sql/shared/src/main/scala/zio/blocks/sql/Table.scala Derived table metadata + DDL helpers (create/drop).
sql/shared/src/main/scala/zio/blocks/sql/SqlOps.scala Minimal query/update execution helpers over DbCon.
sql/shared/src/main/scala/zio/blocks/sql/SqlNameMapper.scala Field-to-column naming strategy implementations.
sql/shared/src/main/scala/zio/blocks/sql/SqlInterpolator.scala DbParam type class + sql"" interpolator + conversion to DbValue.
sql/shared/src/main/scala/zio/blocks/sql/SqlDialect.scala Dialect-specific type-name and placeholder rendering.
sql/shared/src/main/scala/zio/blocks/sql/Frag.scala SQL fragment structure, rendering, and concatenation.
sql/shared/src/main/scala/zio/blocks/sql/Ddl.scala DDL fragment constructors and column definitions.
sql/shared/src/main/scala/zio/blocks/sql/DbValue.scala ADT for parameter values and type mapping input.
sql/shared/src/main/scala/zio/blocks/sql/DbTx.scala Transaction context marker extending DbCon.
sql/shared/src/main/scala/zio/blocks/sql/DbConnection.scala Backend-agnostic connection/statement/result abstractions.
sql/shared/src/main/scala/zio/blocks/sql/DbCon.scala Connection context (dialect + connection handle).
sql/shared/src/main/scala/zio/blocks/sql/DbCodecDeriver.scala Deriver[DbCodec] implementation based on schema reflection/binding.
sql/shared/src/main/scala/zio/blocks/sql/DbCodec.scala Core codec interface + reader/writer traits.
sql/jvm/src/test/scala/zio/blocks/sql/TransactorSpec.scala SQLite in-memory integration tests for transactor/query/update + roundtrips.
sql/jvm/src/test/scala/zio/blocks/sql/.gitkeep Placeholder file for JVM test directory.
sql/jvm/src/main/scala/zio/blocks/sql/JdbcTransactor.scala JDBC transactor implementation for connect/transact.
sql/jvm/src/main/scala/zio/blocks/sql/JdbcResultReader.scala JDBC ResultSet to DbResultReader adapter.
sql/jvm/src/main/scala/zio/blocks/sql/JdbcParamWriter.scala JDBC PreparedStatement to DbParamWriter adapter.
sql/jvm/src/main/scala/zio/blocks/sql/JdbcConnection.scala JDBC connection/statement/result wrappers.
sql/jvm/src/main/scala/zio/blocks/sql/.gitkeep Placeholder file for JVM main directory.
sql/js/src/main/scala/zio/blocks/sql/.gitkeep Placeholder file for JS main directory.
sql-zio/src/main/scala/zio/blocks/sql/zio/TransactorZIO.scala ZIO Task wrappers over blocking Transactor operations.
build.sbt Adds sql crossProject and sql-zio project; aggregates them in root; adds test deps.


def createTable: Frag = {
val columnDefs = codec.columns.map { col =>
ColumnDef(col, dialect.typeName(DbValue.DbString("")), nullable = false)
Comment on lines +2 to +4

trait Transactor {
def connect[A](f: DbCon ?=> A): A
Comment on lines +218 to +222
def readValue(reader: DbResultReader, startIndex: Int): A = {
val innerValue = innerCodec.readValue(reader, startIndex)
val result: Any = if (reader.wasNull) None else Some(innerValue)
result.asInstanceOf[A]
}
Comment on lines +3 to +7
trait DbCodec[A] {
def columns: IndexedSeq[String]
def readValue(reader: DbResultReader, startIndex: Int): A
def writeValue(writer: DbParamWriter, startIndex: Int, value: A): Unit
def toDbValues(value: A): IndexedSeq[DbValue]
Frag(sc.parts.toIndexedSeq, args.toIndexedSeq)
}

given dbParamToDbValue[A](using p: DbParam[A]): Conversion[A, DbValue] = p.toDbValue(_)
Comment on lines +38 to +41
params(i) match {
case DbValue.DbNull => writer.setNull(idx, 0)
case DbValue.DbInt(v) => writer.setInt(idx, v)
case DbValue.DbLong(v) => writer.setLong(idx, v)
Comment on lines +7 to +16
def createTable(tableName: String, columns: IndexedSeq[ColumnDef]): Frag = {
val colDefs = columns.map { col =>
val nullStr = if (col.nullable) "" else " NOT NULL"
s" ${col.name} ${col.sqlType}$nullStr"
}
Frag.const(s"CREATE TABLE IF NOT EXISTS $tableName (\n${colDefs.mkString(",\n")}\n)")
}

def dropTable(tableName: String): Frag =
Frag.const(s"DROP TABLE IF EXISTS $tableName")
Comment on lines +279 to +290
SqlOps.update(Frag.const("CREATE TABLE rt_multi (id INTEGER NOT NULL, name TEXT NOT NULL)"))
SqlOps.update(sql"INSERT INTO rt_multi VALUES (${DbValue.DbInt(1)}, ${DbValue.DbString("a")})")
SqlOps.update(sql"INSERT INTO rt_multi VALUES (${DbValue.DbInt(2)}, ${DbValue.DbString("b")})")
SqlOps.update(sql"INSERT INTO rt_multi VALUES (${DbValue.DbInt(3)}, ${DbValue.DbString("c")})")
val results = SqlOps.query[User](
sql"SELECT id, name, name FROM rt_multi ORDER BY id"
)
assertTrue(
results.length == 3,
results(0).id == 1,
results(1).id == 2,
results(2).id == 3
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new zio-blocks-sql module (plus sql-zio) that provides schema-driven, type-safe SQL fragments, codec derivation, JDBC execution, and optional ZIO wrappers, integrating with existing zio-blocks-schema + zio-blocks-scope.

Changes:

  • Introduces core SQL abstractions (DbValue, Frag, SqlDialect, DbCodec + derivation, Repo, Transactor) in sql/shared.
  • Adds JVM JDBC backend (JdbcTransactor, JDBC adapters) and integration tests using SQLite in-memory.
  • Adds ZIO wrapper module sql-zio (TransactorZIO) and wires new modules into build.sbt.

Reviewed changes

Copilot reviewed 32 out of 37 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
sql/shared/src/test/scala/zio/blocks/sql/TableSpec.scala Tests for table name derivation, pluralization helper, and DDL rendering.
sql/shared/src/test/scala/zio/blocks/sql/SqlNameMapperSpec.scala Tests for snake_case/identity/custom name mappers.
sql/shared/src/test/scala/zio/blocks/sql/SqlInterpolatorSpec.scala Tests for DbParam conversions and sql"..." interpolation behavior.
sql/shared/src/test/scala/zio/blocks/sql/SqlDialectSpec.scala Tests for dialect type-name mapping and parameter placeholder style.
sql/shared/src/test/scala/zio/blocks/sql/RepoSpec.scala Unit tests for Repo SQL fragment builders and basic repo metadata.
sql/shared/src/test/scala/zio/blocks/sql/FragSpec.scala Tests for Frag composition, rendering, and parameter handling.
sql/shared/src/test/scala/zio/blocks/sql/DdlSpec.scala Tests for basic DDL generation helpers.
sql/shared/src/test/scala/zio/blocks/sql/DbValueSpec.scala Tests for DbValue constructors/extractors.
sql/shared/src/test/scala/zio/blocks/sql/DbCodecSpec.scala Tests for schema-driven DbCodec derivation across primitives/records/enums/options/modifiers.
sql/shared/src/test/scala/zio/blocks/sql/.gitkeep Keeps test package directory in VCS.
sql/shared/src/main/scala/zio/blocks/sql/Transactor.scala Defines direct-style transactor API (connect/transact).
sql/shared/src/main/scala/zio/blocks/sql/Table.scala Adds Table metadata/DDL helpers and name derivation + pluralization helper.
sql/shared/src/main/scala/zio/blocks/sql/SqlOps.scala Implements query/update execution against DbCon with logging and param binding.
sql/shared/src/main/scala/zio/blocks/sql/SqlNameMapper.scala Implements column-name mapping strategies (snake_case, identity, custom).
sql/shared/src/main/scala/zio/blocks/sql/SqlLogger.scala Adds logging interface/events and a noop implementation.
sql/shared/src/main/scala/zio/blocks/sql/SqlInterpolator.scala Adds DbParam type class, sql"..." interpolator, and conversions to DbValue.
sql/shared/src/main/scala/zio/blocks/sql/SqlDialect.scala Defines PostgreSQL/SQLite type mapping and placeholder style.
sql/shared/src/main/scala/zio/blocks/sql/Repo.scala Adds a simple CRUD repository built on Frag + DbCodec.
sql/shared/src/main/scala/zio/blocks/sql/Frag.scala Represents composable SQL fragments with dialect rendering and extension ops.
sql/shared/src/main/scala/zio/blocks/sql/Ddl.scala Provides basic CREATE/DROP TABLE fragment builders.
sql/shared/src/main/scala/zio/blocks/sql/DbValue.scala Defines DbValue ADT for SQL parameter values.
sql/shared/src/main/scala/zio/blocks/sql/DbTx.scala Transaction context marker trait extending DbCon.
sql/shared/src/main/scala/zio/blocks/sql/DbConnection.scala Backend-agnostic connection/statement/resultset interfaces.
sql/shared/src/main/scala/zio/blocks/sql/DbCon.scala Connection context containing connection, dialect, and logger.
sql/shared/src/main/scala/zio/blocks/sql/DbCodecDeriver.scala Implements Deriver[DbCodec] based on Schema reflection/bindings.
sql/shared/src/main/scala/zio/blocks/sql/DbCodec.scala Defines DbCodec plus reader/writer interfaces.
sql/jvm/src/test/scala/zio/blocks/sql/TransactorSpec.scala JDBC transactor integration tests (SQLite in-memory) and type roundtrips.
sql/jvm/src/test/scala/zio/blocks/sql/RepoIntegrationSpec.scala End-to-end CRUD tests for Repo + logging using SQLite in-memory.
sql/jvm/src/test/scala/zio/blocks/sql/.gitkeep Keeps JVM test package directory in VCS.
sql/jvm/src/main/scala/zio/blocks/sql/JdbcTransactor.scala JDBC implementation of Transactor using java.sql.Connection.
sql/jvm/src/main/scala/zio/blocks/sql/JdbcResultReader.scala JDBC ResultSet adapter to DbResultReader.
sql/jvm/src/main/scala/zio/blocks/sql/JdbcParamWriter.scala JDBC PreparedStatement adapter to DbParamWriter.
sql/jvm/src/main/scala/zio/blocks/sql/JdbcConnection.scala JDBC adapters for connection/prepared-statement/resultset.
sql/jvm/src/main/scala/zio/blocks/sql/.gitkeep Keeps JVM main package directory in VCS.
sql/js/src/main/scala/zio/blocks/sql/.gitkeep Keeps JS main package directory in VCS.
sql-zio/src/main/scala/zio/blocks/sql/zio/TransactorZIO.scala Adds blocking ZIO wrapper for Transactor.
build.sbt Registers sql crossProject + sql-zio project and dependencies.

Comment on lines +145 to +150
partsB += s"UPDATE $tableName SET ${columns(0)} = "

var i = 1
while (i < columns.size) {
partsB += s", ${columns(i)} = "
i += 1
Comment on lines +27 to +48
def transact[A](f: DbTx ?=> A): A = {
val conn = connectionFactory()
val dbConn = new JdbcConnection(conn)
conn.setAutoCommit(false)
try {
given tx: DbTx = new DbTx {
val connection: DbConnection = dbConn
val dialect: SqlDialect = JdbcTransactor.this.dialect
val logger: SqlLogger = JdbcTransactor.this.sqlLogger
}
val result = f
conn.commit()
result
} catch {
case e: Throwable =>
try conn.rollback()
catch { case rb: Throwable => e.addSuppressed(rb) }
throw e
} finally {
try dbConn.close()
catch { case _: Throwable => () }
}
Comment on lines +214 to +222
// Note: wasNull reflects the last column read by the inner codec.
// For single-column types (the common case), this is correct.
// For multi-column inner types, wasNull only reflects the last column,
// so a NULL in an earlier column may not be detected.
def readValue(reader: DbResultReader, startIndex: Int): A = {
val innerValue = innerCodec.readValue(reader, startIndex)
val result: Any = if (reader.wasNull) None else Some(innerValue)
result.asInstanceOf[A]
}
Comment on lines +2 to +4

trait Transactor {
def connect[A](f: DbCon ?=> A): A
Comment on lines +2 to +51

trait DbCodec[A] {
def columns: IndexedSeq[String]
def readValue(reader: DbResultReader, startIndex: Int): A
def writeValue(writer: DbParamWriter, startIndex: Int, value: A): Unit
def toDbValues(value: A): IndexedSeq[DbValue]
def columnCount: Int = columns.size
}

object DbCodec {
def apply[A](implicit codec: DbCodec[A]): DbCodec[A] = codec
}

trait DbResultReader {
def getInt(index: Int): Int
def getLong(index: Int): Long
def getDouble(index: Int): Double
def getFloat(index: Int): Float
def getBoolean(index: Int): Boolean
def getString(index: Int): String
def getBigDecimal(index: Int): java.math.BigDecimal
def getBytes(index: Int): Array[Byte]
def getShort(index: Int): Short
def getByte(index: Int): Byte
def getLocalDate(index: Int): java.time.LocalDate
def getLocalDateTime(index: Int): java.time.LocalDateTime
def getLocalTime(index: Int): java.time.LocalTime
def getInstant(index: Int): java.time.Instant
def getDuration(index: Int): java.time.Duration
def getUUID(index: Int): java.util.UUID
def wasNull: Boolean
}

trait DbParamWriter {
def setInt(index: Int, value: Int): Unit
def setLong(index: Int, value: Long): Unit
def setDouble(index: Int, value: Double): Unit
def setFloat(index: Int, value: Float): Unit
def setBoolean(index: Int, value: Boolean): Unit
def setString(index: Int, value: String): Unit
def setBigDecimal(index: Int, value: java.math.BigDecimal): Unit
def setBytes(index: Int, value: Array[Byte]): Unit
def setShort(index: Int, value: Short): Unit
def setByte(index: Int, value: Byte): Unit
def setLocalDate(index: Int, value: java.time.LocalDate): Unit
def setLocalDateTime(index: Int, value: java.time.LocalDateTime): Unit
def setLocalTime(index: Int, value: java.time.LocalTime): Unit
def setInstant(index: Int, value: java.time.Instant): Unit
def setDuration(index: Int, value: java.time.Duration): Unit
def setUUID(index: Int, value: java.util.UUID): Unit
Comment on lines +8 to +13
def createTable: Frag = {
val columnDefs = codec.columns.map { col =>
ColumnDef(col, dialect.typeName(DbValue.DbString("")), nullable = false)
}
Ddl.createTable(name, columnDefs)
}
Comment on lines +35 to +38
configured.getOrElse {
val typeName = schema.reflect.typeId.name
SqlNameMapper.SnakeCase(typeName)
}
Comment on lines +29 to +35
def findById(id: ID)(using con: DbCon): Option[E] = {
val frag = Frag(
IndexedSeq(s"SELECT $allCols FROM $tbl WHERE $idColumn = ", ""),
idCodec.toDbValues(id)
)
SqlOps.queryOne[E](frag)(using con, codec)
}
def update(entity: E)(using con: DbCon): Int = {
val entityValues = codec.toDbValues(entity)
val idValues = idCodec.toDbValues(getId(entity))
val frag = Repo.buildUpdateFrag(tbl, table.columns, entityValues, idColumn, idValues)
Comment on lines +121 to +133
private[sql] def buildInsertFrag(
tableName: String,
allColumns: String,
values: IndexedSeq[DbValue]
): Frag =
if (values.isEmpty) Frag.const(s"INSERT INTO $tableName ($allColumns) VALUES ()")
else {
val parts =
IndexedSeq(s"INSERT INTO $tableName ($allColumns) VALUES (") ++
IndexedSeq.fill(values.size - 1)(", ") :+
")"
Frag(parts, values)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

zio-blocks-sql: Schema-driven SQL database module

2 participants