Alec Strong Jake Wharton data class Player(A val name: String, val ranking: Int, val country: String )A @Entity data class Player(A val name: String, val ranking: Int, val country: String )A @Entity data class Player(A val name: String, val ranking: Int, val country: String )A

@Entity data class Match(A val player1: Player, val player2: Player, val winner: Player )A Annotation Processor

@Entity data class Player(A val name: String, val ranking: Int, val country: String )A

@Entity data class Match(A Source val player1: PlayerCode, val player2: Player, val winner: Player )A

Reflection Annotation Processor

@Entity data class Player(A val name: String, val ranking: Int, val country: String )A

@Entity data class Match(A Source val player1: PlayerCode, SQLite val player2: Player, val winner: Player )A

Reflection CREATE player (A name TEXT NOT NULL, country TEXT NOT NULL, ranking INTEGER NOT NULL );

CREATE TABLE match (A player1 INTEGER NOT NULL REFERENCES player, player2 INTEGER NOT NULL REFERENCES player, winner INTEGER NOT NULL REFERENCES player ); CREATE TABLE player (A id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, country TEXT NOT NULL, ranking INTEGER NOT NULL );

CREATE TABLE match (A player1 INTEGER NOT NULL REFERENCES player, player2 INTEGER NOT NULL REFERENCES player, winner INTEGER NOT NULL REFERENCES player );B CREATE TABLE player (A id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, country TEXT NOT NULL, ranking INTEGER NOT NULL );

CREATE TABLE match (A player1 INTEGER NOT NULL REFERENCES player, player2 INTEGER NOT NULL REFERENCES player, winner INTEGER NOT NULL REFERENCES player, PRIMARY KEY (player1, player2) ) WITHOUT ROWID;B Annotation Processor

@Entity CREATE TABLE player (A data class Player( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, val name: String, name TEXT NOT NULL, val ranking: Int, country TEXT NOT NULL, ranking INTEGER NOT NULL val country: String ); ) CREATE TABLE match (A @Entity player1 INTEGER NOT NULL REFERENCES player, Sourcedata class Match( Code player2 INTEGERSQLite NOT NULL REFERENCES player, val player1: Player, winner INTEGER NOT NULL REFERENCES player, val player2: Player, PRIMARY KEY (player1, player2) val winner: Player ) WITHOUT ROWID; )

Reflection @Entity data class Player(A val name: String, val ranking: Int, val country: String )A

@Entity data class Match(A val player1: Player, val player2: Player, val winner: Player )A @Entity data class Player(A @PrimaryKey @Autoincrement val id: Int, val name: String, val ranking: Int, val country: String )A

@Entity data class Match(A val player1: Player, val player2: Player, val winner: Player )A @Entity data class Player(A @PrimaryKey @Autoincrement val id: Int, val name: String, val ranking: Int, val country: String )A

@Entity(primaryKeys = ['player1', ‘player2']) data class Match(A val player1: Player, val player2: Player, val winner: Player )A @Entity dataA class Player(A @PrimaryKey @Autoincrement val id: Int, val name: String, val ranking: Int, val country: String )A

@Entity(primaryKeys = ['player1', ‘player2']) data class Match(A val player1: Player, val player2: Player, val winner: Player )A ALTER TABLE player ADD id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT;

CREATE TABLE new_match ( player1 INTEGER NOT NULL REFERENCES player, player2 INTEGER NOT NULL REFERENCES player, winner INTEGER NOT NULL REFERENCES player, PRIMARY KEY (player1, player2) ) WITHOUT ROWID;

-- migrate match to new_match QueryBuilders

QueryBuilders

jakes.link/resurgence-of- SQL Delight Annotation Processor

Source Code SQLite

Reflection Generated SQLite Compiler Code CREATE TABLE player (A id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, country TEXT NOT NULL, ranking INTEGER NOT NULL ); CREATE TABLE player (A id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, country TEXT NOT NULL, ranking INTEGER NOT NULL ); withRanking: SELECT * FROM player WHERE ranking = ?; CREATE TABLE player (A id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, country TEXT NOT NULL, ranking INTEGER NOT NULL Generated ); withRanking:SQLite Compiler SELECT * FROM player WHERE ranking = ?; Code interface PlayerModel {A long id(); @NonNull String name(); @NonNull String country(); long ranking();

final class Factory {A public Factory(Creator creator);

public SqlDelightQuery withRanking(long ranking);

public RowMapper withRankingMapper(); }A }A interface PlayerModel {A … final class Factory {A public Factory(Creator creator); … }B }A

@AutoValue public abstract class MyPlayerModel implements PlayerModel {A public static Factory FACTORY = new Factory(AutoValue_MyPlayerModel::new) }A interface PlayerModel {A … final class Factory {A public Factory(Creator creator);

public SqlDelightQuery withRanking(long ranking);

public RowMapper withRankingMapper(); }B }A

SqlDelightStatement query = MyPlayerModel.FACTORY.withRanking(10); Observable> rank10 = db.createQuery(query.tables, query.statement, query.args) .mapToList(MyPlayerModel.FACTORY::withRankingMapper); SQL Delight 0.7

QueryBuilders

jakes.link/embracing-sql SQL Delight 1.0 CREATE TABLE player ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, country TEXT NOT NULL, ranking INTEGER NOT NULL ); withRanking: Generated SELECT * FROM player SQLite Compiler WHERE ranking = ?; insertPlayer: INSERT INTO player (name, country, ranking) Code VALUES (?, ?, ?); CREATE TABLE player ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, country TEXT NOT NULL, ranking INTEGER NOT NULL ); withRanking: SELECT * FROM player WHERE ranking = ?; insertPlayer: INSERT INTO player (name, country, ranking) VALUES (?, ?, ?); CREATE TABLE player ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, country TEXT NOT NULL, ranking INTEGER NOT NULL ); withRanking: Generated SELECT * FROM player SQLite Compiler WHERE ranking = ?; insertPlayer: INSERT INTO player (name, country, ranking) Code VALUES (?, ?, ?); interface Player {A val id: Long val name: String val country: String val ranking: Long

data class Impl(A override val id: Long, override val name: String, override val country: String, override val ranking: Long ) : Player }A class PlayerQueries : Transacter {A fun withRanking( ranking: Long, mapper: ( id: Long, name: String, country: String, ranking: Long ) -> T ): Query

fun withRanking(ranking: Long): Query

fun insertPlayer( name: String, country: String, ranking: Long ): Long }A headToHead: SELECT player1.name AS player1Name, player2.name AS player2Name, count(nullif(match.winner = player1.id, 0)) AS player1Wins, count(nullif(match.winner = player2.id, 0)) AS player2Wins FROM match JOIN player AS player1 ON (player1.name = :firstPlayerName AND (player1.id = match.player1 OR player1.id = match.player2) ) JOIN player AS player2 ON (player2.name = :secondPlayerName AND (player2.id = match.player1 OR player2.id = match.player2) ) ; class PlayerQueries : Transacter {A fun headToHead( firstPlayerName: String, secondPlayerName: String, mapper: ( player1Name: String, player2Name: String, player1Wins: Long, player2Wins: Long ) -> T ): Query

fun headToHead( firstPlayerName: String, secondPlayerName: String ): Query }A class PlayerQueries : Transacter {A fun withRanking( ranking: Long, mapper: ( id: Long, name: String, country: String, ranking: Long ) -> T ): Query

fun withRanking(ranking: Long): Query

fun insertPlayer( name: String, country: String, ranking: Long ): Long }A class PlayerQueries : Transacter {A fun withRanking(ranking: Long): Query }A

val player: Query = playerQueries.withRanking(10) class PlayerQueries : Transacter {A fun withRanking(ranking: Long): Query }A

val player: Query< Player => playerQueries.withRanking(10).executeAsOne() class PlayerQueries : Transacter {A fun withRanking(ranking: Long): Query }A

val player: Observable> = playerQueries.withRanking( 10 ) .executeAsOne() .asObservable() class PlayerQueries : Transacter {A fun withRanking(ranking: Long): Query }A

val player: Observable> = playerQueries.withRanking(10) .asObservable() .mapToList() class PlayerQueries : Transacter {A fun withRanking(ranking: Long, mapper: ( id: Long, name: String, country: String, ranking: Long ) -> T ): Query< TPlayer> }A

val player = playerQueries.withRanking(10, ::MyPlayer) class PlayerQueries : Transacter {A fun insertPlayer( name: String, country: String, ranking: Long ): Long }A

val id = playerQueries.insertPlayer( name = "Roger Federer", country = "Switzerland", ranking = 2 ) CREATE TABLE player (A id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, class PlayerQueries : Transacter {A country TEXT NOT NULL, fun insertPlayer( ranking INTEGER NOT NULL Generated name: String, ); country: String, ranking: Long SQLite Compiler ): Long withRanking: }A SELECT * FROM player Code WHERE ranking = ?; CREATE TABLE player ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, country TEXT NOT NULL, ranking INTEGER NOT NULL ); import com.tennis.db.CountryCode;

CREATE TABLE player ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, country TEXT AS CountryCode NOT NULL, ranking INTEGER NOT NULL ); import com.tennis.db.CountryCode;

CREATE TABLE player ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, country TEXT AS CountryCode NOT NULL, ranking INTEGER NOT NULL ); import com.tennis.db.CountryCode;

CREATE TABLE player ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, Generated name TEXT NOT NULL, country TEXTSQLite AS CountryCode NOT NULL, Compiler ranking INTEGER NOT NULL ); Code class QueryWrapper( database: SqlDatabase, internal val playerAdapter: Player.Adapter ) {A val playerQueries: PlayerQueries }A class QueryWrapper( database: SqlDatabase, internal val playerAdapter: Player.Adapter ) {A val playerQueries: PlayerQueries }A interface Player {A class Adapter( val countryAdapter: ColumnAdapter ) }A class QueryWrapper( database: SqlDatabase, internal val playerAdapter: Player.Adapter ) {A val playerQueries: PlayerQueries }A

val queryWrapper = QueryWrapper( database = database, playerAdapter = Player.Adapter( countryAdapter = EnumColumnAdapter() ), ) class QueryWrapper( database: SqlDatabase, internal val playerAdapter: Player.Adapter ) {A val playerQueries: PlayerQueries }A

val queryWrapper = QueryWrapper( database = database, playerAdapter = Player.Adapter( countryAdapter = EnumColumnAdapter() ), ) val playerQueries: PlayerQueries = queryWrapper.playerQueries class QueryWrapper {A object Schema : SqlDatabase.Schema {A override val version: Int get() = 2

override fun create(db: SqlDatabaseConnection)

override fun migrate( db: SqlDatabaseConnection, oldVersion: Int, newVersion: Int ) }A }A class QueryWrapper {A object Schema : SqlDatabase.Schema {A CREATE TABLE player (A override val version: Int id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, get() = 2 name TEXT NOT NULL, country TEXT NOT NULL, override fun create(db: SqlDatabaseConnection) ranking INTEGER NOT NULL Generated ); override fun migrate( db: SqlDatabaseConnection, SQLite Compiler oldVersion: Int, withRanking: newVersion: Int SELECT * ) FROM player }A Code WHERE ranking = ?; }A SQLite

Generated Compiler Code

SQLite Migrations ALTER TABLE player ADD COLUMN id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT;

CREATE TABLE new_match ( player1 INTEGER NOT NULL REFERENCES player, player2 INTEGER NOT NULL REFERENCES player, winner INTEGER NOT NULL REFERENCES player, PRIMARY KEY (player1, player2) ) WITHOUT ROWID;

-- migrate match to new_match SQLite

Generated Compiler Code

ALTER TABLE player ADD COLUMN id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT;

CREATE TABLE new_match ( player1 INTEGERSQLite NOT NULL REFERENCES player, player2 INTEGER NOT NULL REFERENCES player, winner INTEGER NOT NULL REFERENCES player, PRIMARY KEY (player1, player2) ) WITHOUT ROWID; -- migrateMigrations match to new_match SQL Delight 1.0 SQL Delight Multiplatform? SQL Delight Multiplatform? CashApp api proto

viewmodels CashApp presenters

backend db db db

CREATE TABLE player (A id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, country TEXT NOT NULL, ranking INTEGER NOT NULL Generated ); SQLite SQL Delight withRanking: SELECT * FROM player Code WHERE ranking = ?; db

Generated SQLite SQL Delight Code db

Generated SQL Delight Code

SQLite

Generated Compiler Swift Code protos

Generated wire Java

.proto

protoc Generated db

Generated SQL Delight Code

SQLite

Generated Compiler Swift Code db

Generated SQL Delight Code

SQLite kotlinc Android Generated Code Kotlin/Native Native kotlinc Android

Kotlin/Native Native Generated Code kotlinc JVM

kotlin2js JavaScript Android Native

JVM JavaScript Android Native

android.database.* android.database.sqlite.*

JVM JavaScript Android Native

android.database.* sqlite3.h android.database.sqlite.*

JVM JavaScript Android Native

android.database.* sqlite3.h android.database.sqlite.*

JVM JavaScript

java.sql.* org.sqlite.* Android Native

android.database.* sqlite3.h android.database.sqlite.*

JVM JavaScript

java.sql.* org.sqlite.* (xerial) Android Native

android.database.* sqlite3.h android.database.sqlite.*

JVM JavaScript

java.sql.* Node? WASM? Vanilla JS? org.sqlite.* (xerial) android.database.* android.database.sqlite.*Android

sqlite3.hNative Generated Code java.sql.* org.sqlite.* JVM (xerial)

Node? WASM?JavaScript Vanilla JS? android.database.* android.database.sqlite.*Android

sqlite3.hNative Generated Database Code Abstraction java.sql.* org.sqlite.* JVM (xerial)

Node? WASM?JavaScript Vanilla JS? Database Abstraction

SqlDatabase Database Abstraction

SqlDatabase.Schema

SqlDatabase Database Abstraction

SqlDatabase.Schema

SqlDatabase SqlPreparedStatement Database Abstraction

SqlDatabase.Schema

SqlPreparedStatement SqlDatabase

Transacter.Transaction Database Abstraction

SqlDatabase.Schema

SqlPreparedStatement SqlDatabase

Transacter.Transaction Transacter Database Abstraction

SqlDatabase.Schema

SqlPreparedStatement SqlDatabase

Transacter.Transaction Transacter Database Abstraction

SqlDatabase.Schema

SqlPreparedStatement SqlCursor SqlDatabase

Transacter.Transaction Transacter Database Abstraction

SqlDatabase.Schema Query

SqlPreparedStatement SqlCursor SqlDatabase

Transacter.Transaction Transacter android.database.* android.database.sqlite.*Android

sqlite3.hNative Generated SqlDatabaseQ Code S AbstractionSql S Tran T java.sql.* org.sqlite.* JVM (xerial)

Node? WASM?JavaScript Vanilla JS? Android android.database.* Driver android.database.sqlite.*Android

knarch.db sqlite3.h Driver Native Generated Database Code Abstraction JDBC java.sql.* Driver org.sqlite.* JVM (xerial)

JS Driver Node? WASM?JavaScript Vanilla JS? Android android.database.* Driver android.database.sqlite.*Android

knarch.db sqlite3.h Driver Native Database Abstraction JDBC java.sql.* Driver org.sqlite.* JVM (xerial)

JS Driver Node? WASM?JavaScript Vanilla JS? Android AndroidX Sqlite AndroidX Sqlite android.database.* Driver Abstraction Framework Impl android.database.sqlite.*Android knarch.db sqlite3.h Driver Native

JDBC java.sql.* Driver org.sqlite.* JVM (xerial)

JS Driver Node? WASM?JavaScript Vanilla JS? AndroidX Sqlite android.database.* Framework Impl android.database.sqlite.*Android Android AndroidX Sqlite Driver Abstraction

AndroidX android.database.* knarch.db SQLCipher Impl net.sqlcipher.database.*Android sqlite3.h Driver Native

JDBC java.sql.* Driver org.sqlite.* JVM (xerial)

JS Driver Node? WASM?JavaScript Vanilla JS? AndroidX Sqlite android.database.* Framework Impl android.database.sqlite.*Android Android AndroidX Sqlite Driver Abstraction

AndroidX android.database.* knarch.db SQLCipher Impl net.sqlcipher.database.*Android sqlite3.h Driver Native Generated Database Code Abstraction JDBC java.sql.* Driver org.sqlite.* JVM (xerial)

JS Driver Node? WASM?JavaScript Vanilla JS? AndroidX Sqlite android.database.* Framework Impl android.database.sqlite.*Android Android AndroidX Sqlite Driver Abstraction

AndroidX android.database.* knarch.db SQLCipher Impl net.sqlcipher.database.*Android AAA sqlite3.h Driver Native Generated Database Code Abstraction JDBC java.sql.* Driver org.sqlite.* JVM (xerial)

JS Node? WASM? Vanilla JS? Driver JavaScriptBBB Android AndroidX Sqlite Driver Abstraction

AndroidX android.database.* knarch.db SQLCipher Impl net.sqlcipher.database.*Android AAA sqlite3.h Driver Native Generated Database Code Abstraction JDBC java.sql.* Driver org.sqlite.* JVM (xerial)

JS Node? WASM? Vanilla JS? Driver JavaScriptBBB Android AndroidX Sqlite Driver Abstraction

AndroidX android.database.* knarch.db SQLCipher Impl net.sqlcipher.database.*Android sqlite3.h Driver Native Generated Database Code Abstraction JDBC java.sql.* Driver org.sqlite.* JVM (xerial)

Node? WASM?JavaScript Vanilla JS? AndroidX Sqlite android.database.* Framework Impl android.database.sqlite.*Android Android AndroidX Sqlite Driver Abstraction

AndroidX android.database.* knarch.db SQLCipher Impl net.sqlcipher.database.*Android sqlite3.h Driver Generated Database Native Code Abstraction JDBC java.sql.* Driver org.sqlite.* JVM (xerial)

Node?JavaScript WASM? Vanilla JS? AndroidX Sqlite android.database.* Framework Impl android.database.sqlite.*Android Android AndroidX Sqlite Driver Abstraction

AndroidX android.database.* knarch.db SQLCipher Impl net.sqlcipher.database.*Android sqlite3.h Driver Generated Database Native Code Abstraction JDBC java.sql.* Driver org.sqlite.* JVM (xerial)

Node?JavaScript WASM? Vanilla JS? Database Abstraction

SqlDatabase.Schema Query

SqlPreparedStatement SqlCursor SqlDatabase

Transacter.Transaction Transacter Database Abstraction

SqlDatabase.Schema Query

SqlPreparedStatement SqlCursor SqlDatabase

Transacter.Transaction Transacter package com.squareup.sqldelight.db expect interface Closeable { fun close() } expect inline fun T.use(body: (T) -> ): R package com.squareup.sqldelight.db expect interface Closeable { fun close() } expect inline fun T.use(body: (T) -> R): R package com.squareup.sqldelight.db import kotlin.io.use as kotlinIoUse actual typealias Closeable = java.io.Closeable actual inline fun T.use(body: (T) -> R): R = kotlinIoUse(body) package com.squareup.sqldelight.db expect interface Closeable { fun close() } expect inline fun T.use(body: (T) -> R): R package com.squareup.sqldelight.db actual interface Closeable { actual fun close() } actual inline fun T.use(body: (T) -> R): R { // Copy/paste JVM implementation… } apply plugin: 'org.jetbrains.kotlin.platform.common' dependencies { compile 'com.squareup.sqldelight:runtime-common:1.0.0' } apply plugin: 'org.jetbrains.kotlin.platform.jvm' // Or .android dependencies { compile 'com.squareup.sqldelight:runtime-jdk:1.0.0' } apply plugin: 'org.jetbrains.kotlin.platform.js' dependencies { compile 'com.squareup.sqldelight:runtime-js:1.0.0' } apply plugin: 'org.jetbrains.kotlin.platform.common' dependencies { compile 'com.squareup.sqldelight:runtime-common:1.0.0' } apply plugin: 'org.jetbrains.kotlin.platform.jvm' // Or .android dependencies { compile 'com.squareup.sqldelight:runtime-jdk:1.0.0' } apply plugin: 'org.jetbrains.kotlin.platform.js' dependencies { compile 'com.squareup.sqldelight:runtime-js:1.0.0' } apply plugin: 'org.jetbrains.kotlin.platform.native' // ??? apply plugin: 'org.jetbrains.kotlin.platform.common' dependencies { compile 'com.squareup.sqldelight:runtime-common:1.0.0' } apply plugin: 'org.jetbrains.kotlin.platform.jvm' // Or .android dependencies { compile 'com.squareup.sqldelight:runtime-jdk:1.0.0' } apply plugin: 'org.jetbrains.kotlin.platform.js' dependencies { compile 'com.squareup.sqldelight:runtime-js:1.0.0' } apply plugin: 'org.jetbrains.kotlin.platform.native' // nope! apply plugin: 'org.jetbrains.kotlin.multiplatform' dependencies { compile 'com.squareup.sqldelight:runtime:1.0.0' } apply plugin: 'org.jetbrains.kotlin.multiplatform' dependencies { compile 'com.squareup.sqldelight:runtime:1.0.0' }

// settings.gradle enableFeaturePreview('GRADLE_METADATA') com.squareup.sqldelight + runtime com.squareup.sqldelight - runtime runtime-1.0.0-sources.jar runtime-1.0.0-javadoc.jar runtime-1.0.0.pom runtime-1.0.0.module runtime-1.0.0.module

{ "variants": [ { "name": "iosArm64-api", "available-at": { "url": "../../runtime-iosarm64/runtime-iosarm64-1.0.0.module" } }, ... ] } com.squareup.sqldelight - runtime runtime-1.0.0-sources.jar runtime-1.0.0-javadoc.jar runtime-1.0.0.pom runtime-1.0.0.module com.squareup.sqldelight + runtime com.squareup.sqldelight + runtime + runtime-common + runtime-iosarm64 + runtime-iosx64 + runtime-jvm + runtime-js com.squareup.sqldelight + android-driver + runtime + runtime-common + runtime-iosarm64 + runtime-iosx64 + runtime-jvm + runtime-js com.squareup.sqldelight - android-driver android-driver-1.0.0.aar android-driver-1.0.0-javador.jar android-driver-1.0.0-sources.jar android-driver-1.0.0-pom.xml + runtime + runtime-common + runtime-iosarm64 + runtime-iosx64 + runtime-jvm + runtime-js com.squareup.sqldelight - android-driver android-driver-1.0.0.aar android-driver-1.0.0-javador.jar android-driver-1.0.0-sources.jar android-driver-1.0.0-pom.xml + runtime + runtime-common + runtime-iosarm64 + runtime-iosx64 + runtime-jvm + runtime-js com.squareup.sqldelight - android-driver android-driver-1.0.0.aar android-driver-1.0.0-javador.jar android-driver-1.0.0-sources.jar android-driver-1.0.0-pom.xml + runtime + runtime-common + runtime-iosarm64 + runtime-iosx64 + runtime-jvm + runtime-js com.squareup.sqldelight - android-driver android-driver-1.0.0.aar android-driver-1.0.0-javador.jar android-driver-1.0.0-sources.jar android-driver-1.0.0-pom.xml + runtime + runtime-common + runtime-iosarm64 + runtime-iosx64 + runtime-jvm + runtime-js + runtime-jdk apply plugin: ‘com.android.application’ apply plugin: 'org.jetbrains.kotlin.multiplatform' dependencies {A implementation 'com.squareup.sqldelight:android-driver:1.0.0' }A kotlin {A sourceSets {A commonMain {A dependencies {A implementation project(':sqldelight-sample:sample-common') }A }A }A }A apply plugin: ‘com.android.application’ apply plugin: 'org.jetbrains.kotlin.multiplatform' dependenciesA {A implementation 'com.squareup.sqldelight:android-driver:1.0.0''com.squareup.sqldelight:android-driver:1.0.0' }AA kotlin {A sourceSets {A commonMain {A A dependencies {A implementation project(':sqldelight-sample:sample-common') }A }A }A }A apply plugin: ‘com.android.application’ apply plugin: 'org.jetbrains.kotlin.multiplatform' dependencies {A implementation (“com.squareup.sqldelight:android-driver:1.0.0'com.squareup.sqldelight:android-driver:1.0.0')) {A{ A exclude group: 'com.squareup.sqldelight', module: 'runtime-jdk' }A }A kotlin {A sourceSets {A commonMain {A dependencies {A implementation project(':sqldelight-sample:sample-common') }A }A }A }A SqlDatabase.Schema Query

SqlPreparedStatement SqlCursor SqlDatabase

Transacter.Transaction Transacter SqlDatabase.Schema Query

SqlPreparedStatement SqlCursor SqlDatabase

Transacter.Transaction Transacter abstract class Query { }X

SqlDatabase.Schema

SqlPreparedStatement SqlCursor SqlDatabase

Transacter.Transaction Transacter abstract class Query { fun execute(): SqlCursor fun executeAsList(): List fun executeAsOne(): RowType fun executeAsOneOrNull(): RowType? }X abstract class Query { fun execute(): SqlCursor fun executeAsList(): List fun executeAsOne(): RowType fun executeAsOneOrNull(): RowType?

fun addListener(listener: Listener) fun removeListener(listener: Listener)

interface Listener { fun queryResultsChanged() }Y }X abstract class Query { fun execute(): SqlCursor fun executeAsList(): List fun executeAsOne(): RowType fun executeAsOneOrNull(): RowType?

fun addListener(listener: Listener) fun removeListener(listener: Listener)

interface Listener { fun queryResultsChanged() }Y

fun notifyDataChanged() }X val usersQuery: Query = // … val users = usersQuery.executeAsList() // Show users somewhere… val usersQuery: Query = // … usersQuery.addListener(object : Listener { override fun queryResultsChanged() { val users = usersQuery.executeAsList() // Show users somewhere… }G }) usersQuery.notifyDataChanged() val usersQuery: Query = // … val executor = // … usersQuery.addListener(object : Listener { override fun queryResultsChanged() { executor.execute { val users = usersQuery.executeAsList() // Show users somewhere… }G }G }) usersQuery.notifyDataChanged() val usersQuery: Query = // … val executor = // … val mainThread = Handler(Looper.getMainLooper()) usersQuery.addListener(object : Listener { override fun queryResultsChanged() { executor.execute { val users = usersQuery.executeAsList() mainThread.post { // Show users somewhere… } }G }G }) usersQuery.notifyDataChanged() val usersQuery: Query = // … val usersQuery: Query = // … val usersObservable = usersQuery.asObservable() val usersQuery: Query = // … val usersObservable: Observable = usersQuery.asObservable() val usersQuery: Query = // … val usersObservable: Observable> = usersQuery.asObservable() val usersQuery: Query = // … val usersObservable: Observable> Query = usersQuery.asObservable() .map { it.executeAsList() } val usersQuery: Query = // … val usersObservable: Observable> = usersQuery.asObservable() .mapToList() val usersQuery: Query = // … val usersQuery: Query = // … val usersChannel: ReceiveChannel> = usersQuery.asChannel() val usersQuery: Query = // … val usersChannel: ReceiveChannel> Query = usersQuery.asChannel() .map { it.executeAsList() } val usersQuery: Query = // … val usersChannel: ReceiveChannel> = usersQuery.asChannel() .mapToList() suspend fun users() { val usersQuery: Query = // … val usersChannel: ReceiveChannel> = usersQuery.asChannel() .mapToList()

return usersChannel.receive() } suspend fun users() { val usersQuery: Query = // … val usersChannel: ReceiveChannel> = usersQuery.asChannel() .mapToList()

return usersChannel.receive() } suspend fun users() { val usersQuery: Query = // … val usersChannel: ReceiveChannel> = usersQuery.asChannel() .mapToList()

return usersChannel.receive() } val usersQuery: Query = // … val usersQuery: Query = // … val usersLiveData: LiveData> = usersQuery.asLiveData()B val usersQuery: Query = // … val usersLiveData: LiveData> Query = usersQuery.asLiveDataList()B val usersQuery: Query = // … val executor = // … val usersLiveData: LiveData> = usersQuery.asLiveDataList(executor)B val pagedQuery = QueryDataSourceFactory( queryProvider = { limit, offset -> queries.users(limit, offset) }, count = queries.usersCount()) apply plugin: 'com.squareup.sqldelight' apply plugin: 'com.squareup.sqldelight' apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'com.squareup.sqldelight' apply plugin: 'org.jetbrains.kotlin.jvm'

// automatically adds… dependencies { api 'com.squareup.sqldelight:runtime-jdk:1.0.0' }X apply plugin: 'com.squareup.sqldelight' apply plugin: 'org.jetbrains.kotlin.jvm'

// automatically adds… dependencies { api 'com.squareup.sqldelight:runtime-jdk:1.0.0' }X tasks.create('generateSqlDelight', SqlDelightTask) { // … }X tasks.getByName('compileKotlin').dependsOn('generateSqlDelight') apply plugin: 'com.squareup.sqldelight' apply plugin: 'org.jetbrains.kotlin.js' apply plugin: 'com.squareup.sqldelight' apply plugin: 'org.jetbrains.kotlin.js'

// automatically adds… dependencies { api 'com.squareup.sqldelight:runtime-js:1.0.0' }X tasks.create('generateSqlDelight', SqlDelightTask) { // … }X tasks.getByName('compileKotlin').dependsOn('generateSqlDelight') apply plugin: 'com.squareup.sqldelight' apply plugin: 'org.jetbrains.kotlin.multiplatform' apply plugin: 'com.squareup.sqldelight' apply plugin: 'org.jetbrains.kotlin.multiplatform'

// automatically adds… dependencies { api 'com.squareup.sqldelight:runtime:1.0.0' }X tasks.create('generateSqlDelight', SqlDelightTask) { // … }X tasks.getByName('compileKotlin').dependsOn('generateSqlDelight') apply plugin: 'com.squareup.sqldelight' apply plugin: 'org.jetbrains.kotlin.android' apply plugin: 'com.squareup.sqldelight' apply plugin: 'org.jetbrains.kotlin.android'

// automatically adds… dependencies { api 'com.squareup.sqldelight:runtime-jdk:1.0.0' }X tasks.create('generateSqlDelight', SqlDelightTask) { // … }X tasks.getByName('compileKotlin').dependsOn('generateSqlDelight') apply plugin: 'com.squareup.sqldelight' apply plugin : 'org.jetbrains.kotlin.android' multiplatform

// automatically adds… dependencies { api 'com.squareup.sqldelight:runtime-jdk:1.0.0' }X tasks.create('generateDebugSqlDelight', SqlDelightTask) { // … }X tasks.getByName('compileDebugKotlin').dependsOn('generateDebugSqlDelight') tasks.create('generateReleaseSqlDelight', SqlDelightTask) { // … }X tasks.getByName('compileReleaseKotlin').dependsOn('generateReleaseSqlDelight') apply plugin: 'com.squareup.sqldelight' apply plugin : 'org.jetbrains.kotlin.android' multiplatform

// automatically adds… dependencies { api 'com.squareup.sqldelight:runtime-jdk:1.0.0' }X tasks.create('generateDebugSqlDelight', SqlDelightTask) { /* … */ }X tasks.create('generateReleaseSqlDelight', SqlDelightTask) { /* … */ }X tasks.getByName('compileDebugKotlin').dependsOn('generateDebugSqlDelight')tasks.create('verifySqlDelightMigration', VerifyMigrationTask) { // … }X tasks.getByName('compileReleaseKotlin').dependsOn('generateReleaseSqlDelight') IntelliJ Plugin IntelliJ Plugin IntelliJ Plugin Gradle Generated Compiler Plugin Code

IntelliJ Plugin Gradle Generated Compiler Plugin Code

IntelliJ Plugin Gradle Plugin

Generated Compiler Code

IntelliJ Plugin SQLDelight 0.7

Gradle Plugin

antlr Compiler

IntelliJ Plugin SQLDelight 0.7

Gradle Plugin

antlr Compiler

IntelliJ PSI Plugin SQLDelight 1.0

Gradle Plugin

PSI Compiler

IntelliJ Plugin SQLDelight 1.0

Gradle Plugin

sqlite-psi Compiler

IntelliJ Plugin JavaScript Android AndroidX Sqlite Driver Abstraction

AndroidX android.database.* knarch.db SQLCipher Impl net.sqlcipher.database.*Android sqlite3.h Driver Native Generated Database Code Abstraction JDBC java.sql.* Driver org.sqlite.* JVM (xerial)

JS Driver Node? WASM?JavaScript Vanilla JS? Android AndroidX Sqlite Driver Abstraction

AndroidX android.database.* knarch.db SQLCipher Impl net.sqlcipher.database.*Android sqlite3.h Driver Native Generated Database Code Abstraction JDBC java.sql.* Driver org.sqlite.* JVM (xerial)

Node? WASM?JavaScript Vanilla JS? runtime storage

Node

WASM

Vanilla JS runtime storage

Node sqlite3

WASM

Vanilla JS runtime storage

Node sqlite3

WASM sqlite-wasm

Vanilla JS runtime storage

Node sqlite3

WASM sqlite-wasm

Vanilla JS sql.js runtime storage

Node sqlite3 filesystem

WASM sqlite-wasm

Vanilla JS sql.js runtime storage

Node sqlite3 filesystem

WASM sqlite-wasm ???

Vanilla JS sql.js ??? iOS let firstPlace = playerQueries.withRanking( ranking: 1 ).executeAsOne() struct Player {A var id: NSNumber var name: String var country: String var ranking: NSNumber }A let firstPlace = playerQueries.withRanking( ranking: 1 ).executeAsOne() struct Player {A var id: NSNumber var name: String var country: String var ranking: NSNumber }A func createPlayer(id: NSNumber, name: String, country: String, ranking: NSNumber) -> Player {A return Player(id: id, name: name, country: country, ranking: ranking) }A let firstPlace = playerQueries.withRanking( ranking: 1 ).executeAsOne() struct Player {A var id: NSNumber var name: String var country: String var ranking: NSNumber }A func createPlayer(id: NSNumber, name: String, country: String, ranking: NSNumber) -> Player {A return Player(id: id, name: name, country: country, ranking: ranking) }A let firstPlace = playerQueries.withRanking( ranking: 1, mapper: createPlayer ).executeAsOne() as! Player Generated SQL Delight Code

SQLite

Generated Compiler Swift Code Generated SQL Delight Code

SQLite

Generated Compiler Swift Code

withRanking: SELECT name FROM player WHERE ranking = ?; : INSERT INTO player (name, country, ranking) VALUES (?, ?, ?); withRanking: SELECT name FROM player WHERE ranking = ?; insert: INSERT INTO player (name, country, ranking) VALUES (?, ?, ?); private inner class Insert { fun execute(): Long { val result = statement.execute() notifyQueries(queryWrapper.playerQueries.withRanking) return result } }

withRanking: SELECT name FROM player WHERE ranking = ?; insert: INSERT INTO player (name, country, ranking) VALUES (?, ?, ?); private inner class Insert { fun executeexecute():(): Long { val result = statementstatement.execute().execute() notifyQueries(queryWrapper.playerQueries.withRanking) return result } }

withRanking: SELECT name FROM player WHERE ranking = ??;; insert: INSERT INTO player (name,(name, countrycountry,, ranking) VALUES (?(?,, ??,, ?)?);; private inner class Insert { fun execute(): Long { val result = statement.execute() notifyQueries(queryWrapper.playerQueries.withRanking) return result } }

withRanking: SELECT name FROM player WHERE ranking = ?; insert: INSERT INTO player (name(name,, countrycountry,, ranking)ranking) VALUES (?(?,, ??,, ?)?);; Other SQL Dialects 1.0! 1.0! (RC) github.com/square/sqldelight github.com/square/sqldelight

jakes.link/resurgence-of-sql jakes.link/embracing-sql github.com/square/sqldelight

jakes.link/resurgence-of-sql jakes.link/embracing-sql

IntelliJ Plugins – Friday @ 15:15, Berlagezaal @Strongolopolis @JakeWharton