or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

database-io.mddatabase-profiles.mdindex.mdplain-sql.mdqueries.mdtable-definitions.mdtype-mappings.md

database-io.mddocs/

0

# Database I/O Actions

1

2

Composable, asynchronous database actions with effect tracking and transaction support in Slick.

3

4

## Capabilities

5

6

### DBIOAction

7

8

The foundation of Slick's asynchronous database operations with composability and effect tracking.

9

10

```scala { .api }

11

/**

12

* Composable database action with result type R, streaming type S, and effect E

13

* @param R Result type of the action

14

* @param S Streaming capability (NoStream or Streaming[T])

15

* @param E Effect type (Read, Write, Transactional, etc.)

16

*/

17

sealed trait DBIOAction[+R, +S <: NoStream, -E <: Effect] {

18

/** Transform the result of this action */

19

def map[R2](f: R => R2): DBIOAction[R2, S, E]

20

21

/** Compose this action with another action (monadic bind) */

22

def flatMap[R2, S2 <: NoStream, E2 <: Effect](f: R => DBIOAction[R2, S2, E2]): DBIOAction[R2, S2, E with E2]

23

24

/** Run another action after this one, ignoring this action's result */

25

def andThen[R2, S2 <: NoStream, E2 <: Effect](a: DBIOAction[R2, S2, E2]): DBIOAction[R2, S2, E with E2]

26

27

/** Run this action and another action in parallel, combining results */

28

def zip[R2, S2 <: NoStream, E2 <: Effect](a: DBIOAction[R2, S2, E2]): DBIOAction[(R, R2), S with S2, E with E2]

29

30

/** Run a cleanup action after this action, regardless of success or failure */

31

def andFinally[S2 <: NoStream, E2 <: Effect](a: DBIOAction[_, S2, E2]): DBIOAction[R, S with S2, E with E2]

32

33

/** Handle failures in this action */

34

def asTry: DBIOAction[Try[R], S, E]

35

36

/** Recover from failures with an alternative result */

37

def recover[R2 >: R](pf: PartialFunction[Throwable, R2]): DBIOAction[R2, S, E]

38

39

/** Recover from failures with an alternative action */

40

def recoverWith[R2 >: R, S2 <: NoStream, E2 <: Effect](pf: PartialFunction[Throwable, DBIOAction[R2, S2, E2]]): DBIOAction[R2, S with S2, E with E2]

41

42

/** Run this action in a transaction */

43

def transactionally: DBIOAction[R, S, E with Transactional]

44

45

/** Apply a cleanup action when this action fails */

46

def cleanUp[S2 <: NoStream, E2 <: Effect](f: Option[Throwable] => DBIOAction[_, S2, E2]): DBIOAction[R, S with S2, E with E2]

47

48

/** Convert failed action to successful action with exception as result */

49

def failed: DBIOAction[Throwable, S, E]

50

51

/** Filter the result based on a predicate */

52

def filter(p: R => Boolean)(implicit executor: ExecutionContext): DBIOAction[R, NoStream, E]

53

54

/** Transform with partial function, fail if not defined */

55

def collect[R2](pf: PartialFunction[R, R2])(implicit executor: ExecutionContext): DBIOAction[R2, NoStream, E]

56

57

/** Replace result with unit value */

58

def void: DBIOAction[Unit, NoStream, E]

59

60

/** Replace result with given value */

61

def as[A](a: => A): DBIOAction[A, NoStream, E]

62

63

/** Use a pinned database session for this action */

64

def withPinnedSession: DBIOAction[R, S, E]

65

66

/** Add a name for logging purposes */

67

def named(name: String): DBIOAction[R, S, E]

68

}

69

70

/**

71

* Type aliases for common action types

72

*/

73

type DBIO[+R] = DBIOAction[R, NoStream, Effect.All]

74

type StreamingDBIO[+R, +T] = DBIOAction[R, Streaming[T], Effect.All]

75

type ReadAction[+R, +S <: NoStream, -E <: Effect] = DBIOAction[R, S, E with Read]

76

type WriteAction[+R, +S <: NoStream, -E <: Effect] = DBIOAction[R, S, E with Write]

77

```

78

79

**Usage Examples:**

80

81

```scala

82

// Basic action composition

83

val action1: DBIO[Int] = coffees.length.result

84

val action2: DBIO[Seq[Coffee]] = coffees.result

85

86

val combined = for {

87

count <- action1

88

allCoffees <- action2

89

} yield (count, allCoffees)

90

91

// Parallel execution

92

val parallelAction = action1.zip(action2)

93

94

// Error handling

95

val safeAction = coffees.result.asTry.map {

96

case Success(coffees) => s"Found ${coffees.length} coffees"

97

case Failure(ex) => s"Error: ${ex.getMessage}"

98

}

99

100

// Transaction

101

val transactionalAction = (for {

102

_ <- coffees += Coffee("New Coffee", 3.50)

103

_ <- coffees.filter(_.price > 5.0).delete

104

} yield ()).transactionally

105

```

106

107

### Effect System

108

109

Track the types of operations performed by database actions.

110

111

```scala { .api }

112

/**

113

* Base trait for database effects

114

*/

115

sealed trait Effect

116

117

/**

118

* Standard database effects

119

*/

120

object Effect {

121

/** Read operations that don't modify data */

122

sealed trait Read extends Effect

123

124

/** Write operations that modify data */

125

sealed trait Write extends Effect

126

127

/** Operations that require transaction support */

128

sealed trait Transactional extends Effect

129

130

/** Schema operations (DDL) */

131

sealed trait Schema extends Effect

132

133

/** All possible effects */

134

type All = Read with Write with Transactional with Schema

135

}

136

```

137

138

### Streaming

139

140

Handle large result sets with streaming capabilities.

141

142

```scala { .api }

143

/**

144

* Streaming capability marker

145

*/

146

sealed trait StreamingAction[+R, +T] extends DBIOAction[R, Streaming[T], Effect.All]

147

148

/**

149

* No streaming capability

150

*/

151

sealed trait NoStream

152

153

/**

154

* Streaming capability with element type T

155

*/

156

final class Streaming[+T] private[slick] ()

157

```

158

159

**Usage Examples:**

160

161

```scala

162

// Stream large result sets

163

val largeResultStream: StreamingDBIO[_, Coffee] = coffees.result

164

165

// Process streaming results with Akka Streams (example integration)

166

import akka.stream.scaladsl.Source

167

import akka.NotUsed

168

169

val source: Source[Coffee, NotUsed] =

170

Source.fromPublisher(db.stream(coffees.result))

171

172

// Streaming with processing

173

val processedStream = db.stream(coffees.result)

174

.map(coffee => coffee.copy(price = coffee.price * 1.1))

175

.take(1000)

176

```

177

178

### Action Combinators

179

180

Combine and sequence database actions in various ways.

181

182

```scala { .api }

183

object DBIO {

184

/** Create an action that returns the given value */

185

def successful[R](v: R): DBIO[R]

186

187

/** Create a failed action with the given exception */

188

def failed[R](t: Throwable): DBIO[R]

189

190

/** Run a sequence of actions sequentially, returning all results */

191

def sequence[R](actions: Seq[DBIO[R]]): DBIO[Seq[R]]

192

193

/** Run a sequence of actions sequentially, ignoring results */

194

def seq(actions: DBIO[_]*): DBIO[Unit]

195

196

/** Convert a Scala Future to a DBIO action */

197

def from[R](f: Future[R]): DBIO[R]

198

199

/** Create an action from a side-effecting function */

200

def fold[T, R](values: Seq[T], zero: R)(f: (R, T) => DBIO[R]): DBIO[R]

201

202

/** Traverse a sequence, applying an action to each element */

203

def traverse[T, R](values: Seq[T])(f: T => DBIO[R]): DBIO[Seq[R]]

204

}

205

```

206

207

**Usage Examples:**

208

209

```scala

210

// Sequential actions

211

val setupActions = DBIO.seq(

212

coffees.schema.create,

213

coffees += Coffee("Americano", 2.50),

214

coffees += Coffee("Latte", 3.00),

215

coffees += Coffee("Espresso", 2.00)

216

)

217

218

// Batch operations

219

val insertActions = Seq(

220

Coffee("Mocha", 3.50),

221

Coffee("Cappuccino", 3.00),

222

Coffee("Macchiato", 3.25)

223

).map(coffee => coffees += coffee)

224

225

val batchInsert = DBIO.sequence(insertActions)

226

227

// Conditional actions

228

def getCoffeeOrCreate(name: String): DBIO[Coffee] = {

229

coffees.filter(_.name === name).result.headOption.flatMap {

230

case Some(coffee) => DBIO.successful(coffee)

231

case None =>

232

val newCoffee = Coffee(name, 2.50)

233

(coffees += newCoffee).map(_ => newCoffee)

234

}

235

}

236

237

// Traverse pattern

238

val coffeeNames = Seq("Americano", "Latte", "Cappuccino")

239

val findOrCreateAll = DBIO.traverse(coffeeNames)(getCoffeeOrCreate)

240

```

241

242

### Query Execution

243

244

Execute queries and return results in various forms.

245

246

```scala { .api }

247

/**

248

* Query result execution methods

249

*/

250

trait QueryExecutionMethods[E, U, C[_]] {

251

/** Execute query and return all results */

252

def result: StreamingDBIO[C[U], U]

253

254

/** Execute query and return first result */

255

def head: DBIO[U]

256

257

/** Execute query and return optional first result */

258

def headOption: DBIO[Option[U]]

259

260

/** Execute query and return streaming results */

261

def stream: StreamingDBIO[C[U], U]

262

}

263

264

/**

265

* Update/Insert/Delete execution methods

266

*/

267

trait ModifyingExecutionMethods[E] {

268

/** Insert a single row */

269

def += (value: E): DBIO[Int]

270

271

/** Insert multiple rows */

272

def ++= (values: Iterable[E]): DBIO[Int]

273

274

/** Insert or update a row (upsert) */

275

def insertOrUpdate(value: E): DBIO[Int]

276

277

/** Update matching rows */

278

def update: DBIO[Int]

279

280

/** Delete matching rows */

281

def delete: DBIO[Int]

282

}

283

```

284

285

**Usage Examples:**

286

287

```scala

288

// Query execution

289

val allCoffees: DBIO[Seq[Coffee]] = coffees.result

290

val firstCoffee: DBIO[Coffee] = coffees.head

291

val maybeCoffee: DBIO[Option[Coffee]] = coffees.headOption

292

293

// Modifications

294

val insertAction: DBIO[Int] = coffees += Coffee("New Coffee", 3.00)

295

val batchInsertAction: DBIO[Int] = coffees ++= Seq(

296

Coffee("Coffee 1", 2.50),

297

Coffee("Coffee 2", 2.75)

298

)

299

300

val updateAction: DBIO[Int] = coffees

301

.filter(_.name === "Old Coffee")

302

.map(_.name)

303

.update("Updated Coffee")

304

305

val deleteAction: DBIO[Int] = coffees

306

.filter(_.price > 5.0)

307

.delete

308

309

// Upsert

310

val upsertAction: DBIO[Int] = coffees.insertOrUpdate(Coffee("Specialty", 4.50))

311

```

312

313

### Transactions

314

315

Manage database transactions for consistency and atomicity.

316

317

```scala { .api }

318

/**

319

* Transaction isolation levels

320

*/

321

object TransactionIsolation {

322

val ReadUncommitted: Int

323

val ReadCommitted: Int

324

val RepeatableRead: Int

325

val Serializable: Int

326

}

327

328

/**

329

* Transaction methods

330

*/

331

trait DatabaseTransaction {

332

/** Run action in a transaction */

333

def transactionally: DBIOAction[R, S, E with Transactional]

334

335

/** Run action in a transaction with specific isolation level */

336

def withTransactionIsolation(level: Int): DBIOAction[R, S, E with Transactional]

337

}

338

```

339

340

**Usage Examples:**

341

342

```scala

343

// Simple transaction

344

val transferMoney = (for {

345

_ <- accounts.filter(_.id === fromAccountId).map(_.balance).update(fromBalance - amount)

346

_ <- accounts.filter(_.id === toAccountId).map(_.balance).update(toBalance + amount)

347

_ <- transactions += Transaction(fromAccountId, toAccountId, amount)

348

} yield ()).transactionally

349

350

// Transaction with error handling

351

val safeTransfer = transferMoney.asTry.map {

352

case Success(_) => "Transfer completed successfully"

353

case Failure(ex) => s"Transfer failed: ${ex.getMessage}"

354

}

355

356

// Nested transactions (savepoints)

357

val complexTransaction = (for {

358

user <- users += User("Alice")

359

profile <- profiles += Profile(user.id, "Alice's Profile")

360

// Inner transaction that might fail

361

_ <- (orders += Order(user.id, "Failed Item")).transactionally.asTry

362

_ <- orders += Order(user.id, "Success Item")

363

} yield user).transactionally

364

365

// Custom isolation level

366

val highConsistencyAction = coffees.result

367

.withTransactionIsolation(TransactionIsolation.Serializable)

368

```

369

370

### Error Handling

371

372

Handle database errors and exceptions in actions.

373

374

```scala { .api }

375

/**

376

* Error handling methods

377

*/

378

trait ErrorHandling[R, S <: NoStream, E <: Effect] {

379

/** Convert to Try[R] to handle exceptions */

380

def asTry: DBIOAction[Try[R], S, E]

381

382

/** Recover from specific exceptions */

383

def recover[R2 >: R](pf: PartialFunction[Throwable, R2]): DBIOAction[R2, S, E]

384

385

/** Recover with alternative action */

386

def recoverWith[R2 >: R, S2 <: NoStream, E2 <: Effect](

387

pf: PartialFunction[Throwable, DBIOAction[R2, S2, E2]]

388

): DBIOAction[R2, S with S2, E with E2]

389

390

/** Handle both success and failure cases */

391

def andThen[U](pf: PartialFunction[Try[R], U]): DBIOAction[R, S, E]

392

}

393

```

394

395

**Usage Examples:**

396

397

```scala

398

// Try-based error handling

399

val safeQuery = coffees.result.asTry.map {

400

case Success(coffees) => Right(coffees)

401

case Failure(ex) => Left(s"Database error: ${ex.getMessage}")

402

}

403

404

// Recover from specific errors

405

val queryWithFallback = coffees.filter(_.name === "Nonexistent").result.recover {

406

case _: NoSuchElementException => Seq.empty[Coffee]

407

case ex: SQLException => throw new RuntimeException(s"Database error: ${ex.getMessage}")

408

}

409

410

// Recover with alternative action

411

val queryWithAlternative = coffees.filter(_.category === "special").result.recoverWith {

412

case _: SQLException => coffees.take(10).result

413

}

414

415

// Cleanup on error

416

val actionWithCleanup = (for {

417

tempId <- tempTable += TempData("processing")

418

result <- complexProcessing(tempId)

419

_ <- tempTable.filter(_.id === tempId).delete

420

} yield result).cleanUp { errorOpt =>

421

errorOpt.fold(DBIO.successful(()))(ex =>

422

DBIO.seq(

423

tempTable.filter(_.status === "processing").delete,

424

logError(ex.getMessage)

425

)

426

)

427

}

428

```

429

430

## Types

431

432

```scala { .api }

433

sealed trait DBIOAction[+R, +S <: NoStream, -E <: Effect]

434

type DBIO[+R] = DBIOAction[R, NoStream, Effect.All]

435

type StreamingDBIO[+R, +T] = DBIOAction[R, Streaming[T], Effect.All]

436

437

sealed trait Effect

438

object Effect {

439

trait Read extends Effect

440

trait Write extends Effect

441

trait Transactional extends Effect

442

trait Schema extends Effect

443

type All = Read with Write with Transactional with Schema

444

}

445

446

sealed trait NoStream

447

final class Streaming[+T] private[slick] ()

448

449

// Execution result types

450

type DatabasePublisher[T] // Reactive Streams Publisher

451

type StreamingInvoker[R, T] // For streaming execution

452

```