@@ -14,10 +14,10 @@ Add duck4s to your `build.sbt`:
1414
1515``` sbt
1616// Core library
17- libraryDependencies += " com.softinio" %% " duck4s" % " 0.1.3 "
17+ libraryDependencies += " com.softinio" %% " duck4s" % " 0.1.4 "
1818
1919// Optional: cats-effect integration (includes fs2)
20- libraryDependencies += " com.softinio" %% " duck4s-cats-effect" % " 0.1.3 "
20+ libraryDependencies += " com.softinio" %% " duck4s-cats-effect" % " 0.1.4 "
2121```
2222
2323### Mill
@@ -27,13 +27,13 @@ Add duck4s to your `build.mill`:
2727``` scala sc:nocompile
2828// Core library
2929def ivyDeps = Agg (
30- ivy " com.softinio::duck4s::0.1.3 "
30+ ivy " com.softinio::duck4s::0.1.4 "
3131)
3232
3333// Optional: cats-effect integration (includes fs2)
3434def ivyDeps = Agg (
35- ivy " com.softinio::duck4s::0.1.3 " ,
36- ivy " com.softinio::duck4s-cats-effect::0.1.3 "
35+ ivy " com.softinio::duck4s::0.1.4 " ,
36+ ivy " com.softinio::duck4s-cats-effect::0.1.4 "
3737)
3838```
3939
@@ -171,6 +171,129 @@ result match
171171 println(s " Batch operation failed: $error" )
172172```
173173
174+ ### Supported Parameter Types
175+
176+ Duck4s provides first-class support for all common DuckDB column types. The following types can be used with prepared statements (` setXxx ` methods) and batch operations (` addBatch ` tuples) without any manual conversion:
177+
178+ | Scala / Java type | Setter method | DuckDB column type |
179+ | ---| ---| ---|
180+ | ` Int ` | ` setInt ` | ` INTEGER ` |
181+ | ` Long ` | ` setLong ` | ` BIGINT ` |
182+ | ` Double ` | ` setDouble ` | ` DOUBLE ` |
183+ | ` Float ` | ` setFloat ` | ` FLOAT ` |
184+ | ` Boolean ` | ` setBoolean ` | ` BOOLEAN ` |
185+ | ` String ` | ` setString ` | ` VARCHAR ` |
186+ | ` BigDecimal ` | ` setBigDecimal ` | ` DECIMAL ` |
187+ | ` java.sql.Date ` | ` setDate ` | ` DATE ` |
188+ | ` java.sql.Timestamp ` | ` setTimestamp ` | ` TIMESTAMP ` |
189+ | ` java.sql.Types.* ` | ` setNull ` | any (NULL) |
190+ | ` java.util.UUID ` | ` setObject ` | ` UUID ` |
191+ | ` java.time.LocalDate ` | ` setObject ` | ` DATE ` |
192+ | ` java.time.LocalDateTime ` | ` setObject ` | ` TIMESTAMP ` |
193+ | ` java.time.OffsetDateTime ` | ` setObject ` | ` TIMESTAMPTZ ` |
194+ | ` Array[Byte] ` | ` setBytes ` | ` BLOB ` |
195+ | ` Option[T] ` | (any of the above) | nullable variant |
196+
197+ The same types are available when reading results from a ` DuckDBResultSet ` via the corresponding ` getXxx ` methods. For BLOB columns, use ` getBytes(columnLabel) ` which internally wraps the DuckDB-specific ` getBlob ` implementation.
198+
199+ ``` scala sc-compile-with:Imp.scala
200+ val result = DuckDBConnection .withConnection() { conn =>
201+ for
202+ _ <- conn.executeUpdate("""
203+ CREATE TABLE events (
204+ id INTEGER,
205+ name VARCHAR,
206+ score FLOAT,
207+ price DECIMAL(10,2),
208+ recorded DATE,
209+ created TIMESTAMP,
210+ uid UUID,
211+ payload BLOB
212+ )
213+ """ )
214+
215+ ts = java.sql.Timestamp .valueOf(" 2024-06-15 10:30:00" )
216+ date = java.sql.Date .valueOf(" 2024-06-15" )
217+ uuid = java.util.UUID .fromString(" 550e8400-e29b-41d4-a716-446655440000" )
218+ bytes = " hello" .getBytes(" UTF-8" )
219+
220+ _ <- conn.withPreparedStatement(" INSERT INTO events VALUES (?, ?, ?, ?, ?, ?, ?, ?)" ) { stmt =>
221+ for
222+ _ <- stmt.setInt(1 , 1 )
223+ _ <- stmt.setString(2 , " launch" )
224+ _ <- stmt.setFloat(3 , 9.5f )
225+ _ <- stmt.setBigDecimal(4 , BigDecimal (" 19.99" ))
226+ _ <- stmt.setDate(5 , date)
227+ _ <- stmt.setTimestamp(6 , ts)
228+ _ <- stmt.setObject(7 , uuid)
229+ _ <- stmt.setBytes(8 , bytes)
230+ count <- stmt.executeUpdate()
231+ yield count
232+ }
233+
234+ rs <- conn.executeQuery(" SELECT * FROM events WHERE id = 1" )
235+ yield
236+ assert(rs.next())
237+ val retrievedUuid = rs.getObject(" uid" , classOf [java.util.UUID ])
238+ val retrievedBytes = rs.getBytes(" payload" )
239+ rs.close()
240+ }
241+ ```
242+
243+ #### Batch operations with tuples
244+
245+ ` addBatch ` accepts tuples of up to 6 elements, with any combination of the supported types:
246+
247+ ``` scala sc-compile-with:Imp.scala
248+ val result = DuckDBConnection .withConnection() { conn =>
249+ for
250+ _ <- conn.executeUpdate(" CREATE TABLE readings (id INTEGER, ts TIMESTAMP, uid UUID, val FLOAT, dec DECIMAL(10,2), data BLOB)" )
251+
252+ batchResult <- conn.withBatch(" INSERT INTO readings VALUES (?, ?, ?, ?, ?, ?)" ) { batch =>
253+ val ts1 = java.sql.Timestamp .valueOf(" 2024-01-01 00:00:00" )
254+ val uuid1 = java.util.UUID .randomUUID()
255+ for
256+ _ <- batch.addBatch((1 , ts1, uuid1, 1.5f , BigDecimal (" 9.99" ), " a" .getBytes(" UTF-8" )))
257+ result <- batch.executeBatch()
258+ yield result
259+ }
260+ yield batchResult
261+ }
262+ ```
263+
264+ #### Option support for nullable columns
265+
266+ Wrap any supported type in ` Option ` to represent nullable columns — ` Some(value) ` binds the value and ` None ` inserts SQL NULL:
267+
268+ ``` scala sc-compile-with:Imp.scala
269+ val result = DuckDBConnection .withConnection() { conn =>
270+ for
271+ _ <- conn.executeUpdate(" CREATE TABLE contacts (id INTEGER, email VARCHAR)" )
272+ batchResult <- conn.withBatch(" INSERT INTO contacts VALUES (?, ?)" ) { batch =>
273+ for
274+ _ <- batch.addBatch((1 , Option (" alice@example.com" )))
275+ _ <- batch.addBatch((2 , Option .empty[String ]))
276+ r <- batch.executeBatch()
277+ yield r
278+ }
279+ yield batchResult
280+ }
281+ ```
282+
283+ #### Custom ParameterBinder
284+
285+ For types not covered by the built-in binders, implement the ` ParameterBinder ` type class:
286+
287+ ``` scala sc-compile-with:Imp.scala
288+ import com .softinio .duck4s .algebra .{ParameterBinder , DuckDBPreparedStatement , DuckDBError }
289+
290+ case class UserId (value : Long )
291+
292+ given ParameterBinder [UserId ] with
293+ def bind (stmt : DuckDBPreparedStatement , index : Int , value : UserId ): Either [DuckDBError , Unit ] =
294+ stmt.setLong(index, value.value).map(_ => ())
295+ ```
296+
174297### Transactions
175298
176299Use transactions for atomic operations:
0 commit comments