ScalaSql integration
The scalasql module lets you point scalasql at a live JDBC database and get a fully typed, query-pushdown-capable table — schema inferred at compile time, no case class or codegen step required.
Assuming you have a postgres database running locally (e.g. world), that is identified by the following three variables;
"SCAUTABLE_DB_USER": "testuser",
"SCAUTABLE_DB_PASSWORD": "testpass",
"SCAUTABLE_DB_URL": "jdbc:postgresql://localhost:5432/testdb"
If you are using scala-cli, you should put these in ide-envs.json. Restart bloop - scala-cli bloop exit...
//> using dep io.github.quafadas::scautable-scalasql:0.0.37-5-22d216
//> using compileOnly.dep io.github.quafadas::scautable-scalasql:0.0.37-5-22d216
//> using dep org.postgresql:postgresql:42.7.13
//> using compileOnly.dep org.postgresql:postgresql:42.7.13
import io.github.quafadas.scautable.scalasql.*
import io.github.quafadas.scautable.db.DB
import io.github.quafadas.scautable.db.Postgres
import scalasql.PostgresDialect.*
import scalasql.simple.{*, given}
import io.github.quafadas.table.*
@main def run(): Unit = {
val db = DB.connection[Postgres]
val country = DB.sqlTable[Postgres]("country")
val city = DB.sqlTable[Postgres]("city")
val countrylanguage = DB.sqlTable[Postgres]("countrylanguage")
db.run(country.select.take(10)).ptbln
db.run(city.select.take(10)).ptbln
db.run(countrylanguage.select.take(10)).ptbln
db.run(city.select.sumBy(_.population))
}
// TO CHECK IF YOUR CONNECTION IS WORKING, you can also use a raw JDBC connection:
// val dataSource = new org.postgresql.ds.PGSimpleDataSource
// dataSource.setURL("jdbc:postgresql://localhost:5432/testdb");
// dataSource.setUser("testuser");
// dataSource.setPassword("testpass");
// lazy val postgresClient = new scalasql.DbClient.DataSource(
// dataSource,
// config = new scalasql.Config {}
// )
// postgresClient.transaction { db =>
// db.runRaw[Int]("SELECT 1")
// }
Connection configuration
Schema inference (compile time) and the runtime DbApi are both configured from the same three environment variables:
SCAUTABLE_DB_URL, SCAUTABLE_DB_USER, SCAUTABLE_DB_PASSWORD.
You select a dialect via a DbFlavour marker type (H2, Postgres, MsSqlServer, ...) — this determines both the scalasql dialect used to build queries and which TypeMappers are summoned for each column.
DB.sqlTable — infer a table's schema
DB.sqlTable[F]("tableName") reads the schema of tableName from the database at compile time and returns a NamedTupleTable — a scalasql Table whose row type is an anonymous NamedTuple. tableName may include a schema qualifier: "schema.table" or just "table".
Constructing countries never opens a network connection — it only runs at compile time against the schema-inference connection (or a snapshot).
DB.connection — get a matching DbApi
DB.connection[F] gives you a lazily-connecting scalasql DbApi, wired from the same SCAUTABLE_DB_URL / _USER / _PASSWORD names, for the dialect F. It is not table-specific, so it takes no table name — call it once and reuse it across tables:
val db = DB.connection[H2]
val big = db.run(countries.select.filter(_.population > 1_000_000))
The underlying JDBC connection is opened lazily on first use (e.g. the first db.run(...) call), not when db is constructed.
DB.sqlConnectionAndTable — both at once
If you want the table and a matching connection together, DB.sqlConnectionAndTable[F]("tableName") returns a (DbApi, NamedTupleTable) pair in one call:
val (db, countries) = DB.sqlConnectionAndTable[H2]("country")
db.run(countries.select.filter(_.population > 1_000_000))
Full query DSL
Because NamedTupleTable is a genuine scalasql Table, the whole query DSL is available: select, filter, map, join, sumBy / avgBy / minBy / maxBy / countBy, insert, update, delete, and so on. Column identifiers are exactly the DB column names — no camelCase mapping is applied.
Identifier handling
Table names are always quoted via the dialect's escape mechanism, and column names are passed through unmodified. If you configure your own scalasql.Config, use columnNameMapper = identity to avoid any camelCase-to-snake_case conversion.