From 46838fbc90ffa96466da952384150df58df663f7 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Fri, 15 Jul 2022 17:34:31 +0200 Subject: [PATCH 01/19] Fix DEFAULT in binding Add test for PostgreSQL Spotless Move mixin to sqldelight Use mixin instead Don't add DEFAULT bindings to query Fix DEFAULT in binding --- .../sqldelight/dialects/hsql/grammar/hsql.bnf | 1 + .../hsql/grammar/mixins/BindParameterMixin.kt | 9 +++ .../dialects/mysql/grammar/MySql.bnf | 1 + .../postgresql/grammar/PostgreSql.bnf | 3 +- .../grammar/mixins/BindParameterMixin.kt | 9 +++ .../postgres/PostgreSqlFixturesTest.kt | 1 + .../dialects/sqlite_3_18/grammar/sqlite.bnf | 1 + .../grammar/mixins/BindParameterMixin.kt | 23 ++++++++ .../core/compiler/QueryGenerator.kt | 56 ++++++++++--------- .../core/compiler/model/BindableQuery.kt | 36 ++++++------ .../cash/sqldelight/hsql/integration/Dog.sq | 2 +- .../sqldelight/hsql/integration/HsqlTest.kt | 2 +- .../sqldelight/postgresql/integration/Dog.sq | 6 +- .../postgresql/integration/PostgreSqlTest.kt | 6 +- 14 files changed, 105 insertions(+), 51 deletions(-) create mode 100644 dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt create mode 100644 dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt create mode 100644 sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt diff --git a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf index 6cce18d0de3..b4f77a56edf 100644 --- a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf +++ b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf @@ -80,6 +80,7 @@ column_constraint ::= [ CONSTRAINT {identifier} ] ( override = true } bind_parameter ::= ( DEFAULT | '?' | ':' {identifier} ) { + mixin = "app.cash.sqldelight.dialects.hsql.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" override = true diff --git a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt new file mode 100644 index 00000000000..ed59bfc1fe9 --- /dev/null +++ b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt @@ -0,0 +1,9 @@ +package app.cash.sqldelight.dialects.hsql.grammar.mixins + +import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin +import com.intellij.lang.ASTNode + +abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { + override val replaceWith: String = if (text == "DEFAULT") text else "?" + override val isDefault = text == "DEFAULT" +} diff --git a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf index 9230ea0ec3d..59e5472b200 100644 --- a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf +++ b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf @@ -112,6 +112,7 @@ column_constraint ::= [ CONSTRAINT {identifier} ] ( override = true } bind_parameter ::= ( '?' | ':' {identifier} ) { + mixin = "app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" override = true diff --git a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf index 50f85881755..d2c77428d89 100644 --- a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf +++ b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf @@ -111,6 +111,7 @@ type_name ::= ( override = true } bind_parameter ::= ( DEFAULT | '?' | ':' {identifier} ) { + mixin = "app.cash.sqldelight.dialects.postgresql.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" override = true @@ -118,7 +119,7 @@ bind_parameter ::= ( DEFAULT | '?' | ':' {identifier} ) { identity_clause ::= 'IDENTITY' -generated_clause ::= GENERATED ( (ALWAYS AS <> 'STORED') | ( (ALWAYS | BY DEFAULT) AS identity_clause ) ) { +generated_clause ::= GENERATED ( (ALWAYS AS '(' <> ')' 'STORED') | ( (ALWAYS | BY DEFAULT) AS identity_clause ) ) { extends = "com.alecstrong.sql.psi.core.psi.impl.SqlGeneratedClauseImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlGeneratedClause" override = true diff --git a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt new file mode 100644 index 00000000000..2381cef92ad --- /dev/null +++ b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt @@ -0,0 +1,9 @@ +package app.cash.sqldelight.dialects.postgresql.grammar.mixins + +import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin +import com.intellij.lang.ASTNode + +abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { + override val replaceWith: String = if (text == "DEFAULT") text else "?" + override val isDefault = text == "DEFAULT" +} diff --git a/dialects/postgresql/src/test/kotlin/app/cash/sqldelight/dialects/postgres/PostgreSqlFixturesTest.kt b/dialects/postgresql/src/test/kotlin/app/cash/sqldelight/dialects/postgres/PostgreSqlFixturesTest.kt index 3443e8b9e90..1a332d2dd64 100644 --- a/dialects/postgresql/src/test/kotlin/app/cash/sqldelight/dialects/postgres/PostgreSqlFixturesTest.kt +++ b/dialects/postgresql/src/test/kotlin/app/cash/sqldelight/dialects/postgres/PostgreSqlFixturesTest.kt @@ -15,6 +15,7 @@ class PostgreSqlFixturesTest(name: String, fixtureRoot: File) : FixturesTest(nam "?1" to "?", "?2" to "?", "BLOB" to "TEXT", + "id TEXT GENERATED ALWAYS AS (2) UNIQUE NOT NULL" to "id TEXT GENERATED ALWAYS AS (2) STORED UNIQUE NOT NULL", ) override fun setupDialect() { diff --git a/dialects/sqlite-3-18/src/main/kotlin/app/cash/sqldelight/dialects/sqlite_3_18/grammar/sqlite.bnf b/dialects/sqlite-3-18/src/main/kotlin/app/cash/sqldelight/dialects/sqlite_3_18/grammar/sqlite.bnf index 2f0c664fd28..4297b957696 100644 --- a/dialects/sqlite-3-18/src/main/kotlin/app/cash/sqldelight/dialects/sqlite_3_18/grammar/sqlite.bnf +++ b/dialects/sqlite-3-18/src/main/kotlin/app/cash/sqldelight/dialects/sqlite_3_18/grammar/sqlite.bnf @@ -27,6 +27,7 @@ int_data_type ::= 'INTEGER' real_data_type ::= 'REAL' bind_parameter ::= ( '?' [digit] | ':' {identifier} ) { + mixin = "app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" override = true diff --git a/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt b/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt new file mode 100644 index 00000000000..bf2aef6ede2 --- /dev/null +++ b/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt @@ -0,0 +1,23 @@ +package app.cash.sqldelight.dialect.grammar.mixins + +import com.alecstrong.sql.psi.core.psi.SqlBindParameter +import com.alecstrong.sql.psi.core.psi.SqlCompositeElementImpl +import com.intellij.lang.ASTNode + +abstract class BindParameterMixin(node: ASTNode) : SqlCompositeElementImpl(node), SqlBindParameter { + /** + * Overwrite, if the user provided sql parameter should be overwritten by sqldelight with [replaceWith]. + * + * Some sql dialects support other bind parameter besides `?`, but sqldelight should still replace the + * user provided parameter with [replaceWith] for a homogen generated code. + */ + open val replaceWith: String = "?" + + /** + * If true, this parameter is not used by code generators during parameter mapping. + * + * Some sql dialects support generated and default columns, so sqldelight should skip this parameter to not + * require and map it from the given parameters. + */ + open val isDefault: Boolean = false +} diff --git a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt index c223709b2d5..10694aa3e39 100644 --- a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt +++ b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt @@ -19,6 +19,7 @@ import app.cash.sqldelight.core.lang.util.rawSqlText import app.cash.sqldelight.core.lang.util.sqFile import app.cash.sqldelight.core.psi.SqlDelightStmtClojureStmtList import app.cash.sqldelight.dialect.api.IntermediateType +import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin import com.alecstrong.sql.psi.core.psi.SqlBinaryEqualityExpr import com.alecstrong.sql.psi.core.psi.SqlBindExpr import com.alecstrong.sql.psi.core.psi.SqlStmt @@ -174,36 +175,39 @@ abstract class QueryGenerator( precedingArrays.add(type.name) argumentCounts.add("${type.name}.size") } else { - nonArrayBindArgsCount += 1 - - if (!treatNullAsUnknownForEquality && type.javaType.isNullable) { - val parent = bindArg?.parent - if (parent is SqlBinaryEqualityExpr) { - needsFreshStatement = true - - var symbol = parent.childOfType(SqlTypes.EQ) ?: parent.childOfType(SqlTypes.EQ2) - val nullableEquality: String - if (symbol != null) { - nullableEquality = "${symbol.leftWhitspace()}IS${symbol.rightWhitespace()}" - } else { - symbol = parent.childOfType(SqlTypes.NEQ) ?: parent.childOfType(SqlTypes.NEQ2)!! - nullableEquality = "${symbol.leftWhitspace()}IS NOT${symbol.rightWhitespace()}" + val bindParameter = bindArg?.bindParameter as? BindParameterMixin + if (bindParameter == null || !bindParameter.isDefault) { + nonArrayBindArgsCount += 1 + + if (!treatNullAsUnknownForEquality && type.javaType.isNullable) { + val parent = bindArg?.parent + if (parent is SqlBinaryEqualityExpr) { + needsFreshStatement = true + + var symbol = parent.childOfType(SqlTypes.EQ) ?: parent.childOfType(SqlTypes.EQ2) + val nullableEquality: String + if (symbol != null) { + nullableEquality = "${symbol.leftWhitspace()}IS${symbol.rightWhitespace()}" + } else { + symbol = parent.childOfType(SqlTypes.NEQ) ?: parent.childOfType(SqlTypes.NEQ2)!! + nullableEquality = "${symbol.leftWhitspace()}IS NOT${symbol.rightWhitespace()}" + } + + val block = CodeBlock.of("if (${type.name} == null) \"$nullableEquality\" else \"${symbol.text}\"") + replacements.add(symbol.range to "\${ $block }") } - - val block = CodeBlock.of("if (${type.name} == null) \"$nullableEquality\" else \"${symbol.text}\"") - replacements.add(symbol.range to "\${ $block }") } - } - // Binds each parameter to the statement: - // statement.bindLong(0, id) - bindStatements.add(type.preparedStatementBinder(offset, extractedVariables[type])) + // Binds each parameter to the statement: + // statement.bindLong(0, id) + bindStatements.add(type.preparedStatementBinder(offset, extractedVariables[type])) - // Replace the named argument with a non named/indexed argument. - // This allows us to use the same algorithm for non Sqlite dialects - // :name becomes ? - if (bindArg != null) { - replacements.add(bindArg.range to "?") + // Replace the named argument with a non named/indexed argument. + // This allows us to use the same algorithm for non Sqlite dialects + // :name becomes ? + if (bindParameter != null) { + replacements.add(bindArg.range to bindParameter.replaceWith) + } } } } diff --git a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt index b7de29ff05e..839209bbbcf 100644 --- a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt +++ b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt @@ -31,6 +31,7 @@ import app.cash.sqldelight.dialect.api.PrimitiveType.ARGUMENT import app.cash.sqldelight.dialect.api.PrimitiveType.BOOLEAN import app.cash.sqldelight.dialect.api.PrimitiveType.INTEGER import app.cash.sqldelight.dialect.api.PrimitiveType.NULL +import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin import com.alecstrong.sql.psi.core.psi.SqlAnnotatedElement import com.alecstrong.sql.psi.core.psi.SqlBindExpr import com.alecstrong.sql.psi.core.psi.SqlBindParameter @@ -91,29 +92,32 @@ abstract class BindableQuery( val namesSeen = mutableSetOf() var maxIndexSeen = 0 statement.findChildrenOfType().forEach { bindArg -> - bindArg.bindParameter.node.findChildByType(SqlTypes.DIGIT)?.text?.toInt()?.let { index -> - if (!indexesSeen.add(index)) { - result.findAndReplace(bindArg, index) { it.index == index } + val bindParameter = bindArg.bindParameter + if (bindParameter is BindParameterMixin && !bindParameter.isDefault) { + bindParameter.node.findChildByType(SqlTypes.DIGIT)?.text?.toInt()?.let { index -> + if (!indexesSeen.add(index)) { + result.findAndReplace(bindArg, index) { it.index == index } + return@forEach + } + maxIndexSeen = maxOf(maxIndexSeen, index) + result.add(Argument(index, typeResolver.argumentType(bindArg), mutableListOf(bindArg))) return@forEach } - maxIndexSeen = maxOf(maxIndexSeen, index) - result.add(Argument(index, typeResolver.argumentType(bindArg), mutableListOf(bindArg))) - return@forEach - } - bindArg.bindParameter.identifier?.let { - if (!namesSeen.add(it.text)) { - result.findAndReplace(bindArg) { (_, type, _) -> type.name == it.text } + bindParameter.identifier?.let { + if (!namesSeen.add(it.text)) { + result.findAndReplace(bindArg) { (_, type, _) -> type.name == it.text } + return@forEach + } + val index = ++maxIndexSeen + indexesSeen.add(index) + manuallyNamedIndexes.add(index) + result.add(Argument(index, typeResolver.argumentType(bindArg).copy(name = it.text), mutableListOf(bindArg))) return@forEach } val index = ++maxIndexSeen indexesSeen.add(index) - manuallyNamedIndexes.add(index) - result.add(Argument(index, typeResolver.argumentType(bindArg).copy(name = it.text), mutableListOf(bindArg))) - return@forEach + result.add(Argument(index, typeResolver.argumentType(bindArg), mutableListOf(bindArg))) } - val index = ++maxIndexSeen - indexesSeen.add(index) - result.add(Argument(index, typeResolver.argumentType(bindArg), mutableListOf(bindArg))) } // If there are still naming conflicts (edge case where the name we generate is the same as diff --git a/sqldelight-gradle-plugin/src/test/integration-hsql/src/main/sqldelight/app/cash/sqldelight/hsql/integration/Dog.sq b/sqldelight-gradle-plugin/src/test/integration-hsql/src/main/sqldelight/app/cash/sqldelight/hsql/integration/Dog.sq index f1d41860df4..e8a76594385 100644 --- a/sqldelight-gradle-plugin/src/test/integration-hsql/src/main/sqldelight/app/cash/sqldelight/hsql/integration/Dog.sq +++ b/sqldelight-gradle-plugin/src/test/integration-hsql/src/main/sqldelight/app/cash/sqldelight/hsql/integration/Dog.sq @@ -8,7 +8,7 @@ CREATE TABLE dog ( insertDog: INSERT INTO dog (name, breed, is_good, id) -VALUES (?, ?, ?, ?); +VALUES (?, ?, DEFAULT, ?); selectDogs: SELECT * diff --git a/sqldelight-gradle-plugin/src/test/integration-hsql/src/test/kotlin/app/cash/sqldelight/hsql/integration/HsqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-hsql/src/test/kotlin/app/cash/sqldelight/hsql/integration/HsqlTest.kt index 94cd092ee67..3068d964ad3 100644 --- a/sqldelight-gradle-plugin/src/test/integration-hsql/src/test/kotlin/app/cash/sqldelight/hsql/integration/HsqlTest.kt +++ b/sqldelight-gradle-plugin/src/test/integration-hsql/src/test/kotlin/app/cash/sqldelight/hsql/integration/HsqlTest.kt @@ -29,7 +29,7 @@ class HsqlTest { } @Test fun simpleSelect() { - database.dogQueries.insertDog("Tilda", "Pomeranian", true, 1) + database.dogQueries.insertDog("Tilda", "Pomeranian", 1) assertThat(database.dogQueries.selectDogs().executeAsOne()) .isEqualTo( Dog( diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/Dog.sq b/sqldelight-gradle-plugin/src/test/integration-postgresql/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/Dog.sq index 77c09eda934..138341ababe 100644 --- a/sqldelight-gradle-plugin/src/test/integration-postgresql/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/Dog.sq +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/Dog.sq @@ -6,7 +6,7 @@ CREATE TABLE dog ( insertDog: INSERT INTO dog -VALUES (?, ?, ?); +VALUES (?, ?, DEFAULT); selectDogs: SELECT * @@ -19,5 +19,5 @@ WHERE :someBoolean AND 1 = 1; insertAndReturn: INSERT INTO dog -VALUES (?, ?, ?) -RETURNING *; \ No newline at end of file +VALUES (?, ?, DEFAULT) +RETURNING *; diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-postgresql/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt index 28ebb8b8957..7c21fac9725 100644 --- a/sqldelight-gradle-plugin/src/test/integration-postgresql/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt @@ -36,7 +36,7 @@ class PostgreSqlTest { } @Test fun simpleSelect() { - database.dogQueries.insertDog("Tilda", "Pomeranian", 1) + database.dogQueries.insertDog("Tilda", "Pomeranian") assertThat(database.dogQueries.selectDogs().executeAsOne()) .isEqualTo( Dog( @@ -48,7 +48,7 @@ class PostgreSqlTest { } @Test fun booleanSelect() { - database.dogQueries.insertDog("Tilda", "Pomeranian", 1) + database.dogQueries.insertDog("Tilda", "Pomeranian") assertThat(database.dogQueries.selectGoodDogs(true).executeAsOne()) .isEqualTo( Dog( @@ -60,7 +60,7 @@ class PostgreSqlTest { } @Test fun returningInsert() { - assertThat(database.dogQueries.insertAndReturn("Tilda", "Pomeranian", 1).executeAsOne()) + assertThat(database.dogQueries.insertAndReturn("Tilda", "Pomeranian").executeAsOne()) .isEqualTo( Dog( name = "Tilda", From 293db312f4e467f98f3ba2f737dce22b3c64c297 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Thu, 22 Sep 2022 12:06:26 +0200 Subject: [PATCH 02/19] Remove isDefault api --- .../dialects/hsql/grammar/mixins/BindParameterMixin.kt | 1 - .../postgresql/grammar/mixins/BindParameterMixin.kt | 1 - .../dialect/grammar/mixins/BindParameterMixin.kt | 8 -------- .../app/cash/sqldelight/core/compiler/QueryGenerator.kt | 2 +- .../cash/sqldelight/core/compiler/model/BindableQuery.kt | 2 +- 5 files changed, 2 insertions(+), 12 deletions(-) diff --git a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt index ed59bfc1fe9..d94f1e65dd8 100644 --- a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt +++ b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt @@ -5,5 +5,4 @@ import com.intellij.lang.ASTNode abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { override val replaceWith: String = if (text == "DEFAULT") text else "?" - override val isDefault = text == "DEFAULT" } diff --git a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt index 2381cef92ad..4598e8194e9 100644 --- a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt +++ b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt @@ -5,5 +5,4 @@ import com.intellij.lang.ASTNode abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { override val replaceWith: String = if (text == "DEFAULT") text else "?" - override val isDefault = text == "DEFAULT" } diff --git a/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt b/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt index bf2aef6ede2..7461ee48bf1 100644 --- a/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt +++ b/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt @@ -12,12 +12,4 @@ abstract class BindParameterMixin(node: ASTNode) : SqlCompositeElementImpl(node) * user provided parameter with [replaceWith] for a homogen generated code. */ open val replaceWith: String = "?" - - /** - * If true, this parameter is not used by code generators during parameter mapping. - * - * Some sql dialects support generated and default columns, so sqldelight should skip this parameter to not - * require and map it from the given parameters. - */ - open val isDefault: Boolean = false } diff --git a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt index 10694aa3e39..c5fe62ddaf5 100644 --- a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt +++ b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt @@ -176,7 +176,7 @@ abstract class QueryGenerator( argumentCounts.add("${type.name}.size") } else { val bindParameter = bindArg?.bindParameter as? BindParameterMixin - if (bindParameter == null || !bindParameter.isDefault) { + if (bindParameter == null || bindParameter.text != "DEFAULT") { nonArrayBindArgsCount += 1 if (!treatNullAsUnknownForEquality && type.javaType.isNullable) { diff --git a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt index 839209bbbcf..afd5d7f10ef 100644 --- a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt +++ b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt @@ -93,7 +93,7 @@ abstract class BindableQuery( var maxIndexSeen = 0 statement.findChildrenOfType().forEach { bindArg -> val bindParameter = bindArg.bindParameter - if (bindParameter is BindParameterMixin && !bindParameter.isDefault) { + if (bindParameter is BindParameterMixin && bindParameter.text != "DEFAULT") { bindParameter.node.findChildByType(SqlTypes.DIGIT)?.text?.toInt()?.let { index -> if (!indexesSeen.add(index)) { result.findAndReplace(bindArg, index) { it.index == index } From 5c9670af08d0fc5d32eb4aa5ee2ba7c21fa9bc3c Mon Sep 17 00:00:00 2001 From: hfhbd Date: Thu, 22 Sep 2022 12:11:37 +0200 Subject: [PATCH 03/19] Fix grammar --- .../cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf index d2c77428d89..dd471ea1754 100644 --- a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf +++ b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf @@ -119,7 +119,7 @@ bind_parameter ::= ( DEFAULT | '?' | ':' {identifier} ) { identity_clause ::= 'IDENTITY' -generated_clause ::= GENERATED ( (ALWAYS AS '(' <> ')' 'STORED') | ( (ALWAYS | BY DEFAULT) AS identity_clause ) ) { +generated_clause ::= GENERATED ( (ALWAYS AS LP <> RP 'STORED') | ( (ALWAYS | BY DEFAULT) AS identity_clause ) ) { extends = "com.alecstrong.sql.psi.core.psi.impl.SqlGeneratedClauseImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlGeneratedClause" override = true From ad350f2157d3100d55f696312ccf0124fc99fb77 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Thu, 22 Sep 2022 15:54:47 +0200 Subject: [PATCH 04/19] Add mysql async test --- .../hsql/grammar/mixins/BindParameterMixin.kt | 2 +- .../cash/sqldelight/dialects/mysql/grammar/MySql.bnf | 2 +- .../mysql/grammar/mixins/BindParameterMixin.kt | 11 +++++++++++ .../postgresql/grammar/mixins/BindParameterMixin.kt | 6 +++++- .../dialect/grammar/mixins/BindParameterMixin.kt | 2 +- .../cash/sqldelight/core/compiler/QueryGenerator.kt | 2 +- .../sqldelight/dialect/DialectIntegrationTests.kt | 9 +++++++++ .../src/test/integration-mysql-async/build.gradle | 4 ++-- .../src/test/integration-mysql-async/settings.gradle | 2 +- .../sqldelight/mysql/integration/{ => async}/Dates.sq | 0 .../sqldelight/mysql/integration/{ => async}/Dog.sq | 0 .../mysql/integration/{ => async}/MySqlTest.kt | 11 +++++++---- 12 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/mixins/BindParameterMixin.kt rename sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/{ => async}/Dates.sq (100%) rename sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/{ => async}/Dog.sq (100%) rename sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/{ => async}/MySqlTest.kt (79%) diff --git a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt index d94f1e65dd8..75148787bf5 100644 --- a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt +++ b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt @@ -4,5 +4,5 @@ import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin import com.intellij.lang.ASTNode abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { - override val replaceWith: String = if (text == "DEFAULT") text else "?" + override fun replaceWith(isAsync: Boolean, index: Int): String = if (text == "DEFAULT") text else "?" } diff --git a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf index 59e5472b200..a9759bf100c 100644 --- a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf +++ b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf @@ -112,7 +112,7 @@ column_constraint ::= [ CONSTRAINT {identifier} ] ( override = true } bind_parameter ::= ( '?' | ':' {identifier} ) { - mixin = "app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin" + mixin = "app.cash.sqldelight.dialects.mysql.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" override = true diff --git a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/mixins/BindParameterMixin.kt b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/mixins/BindParameterMixin.kt new file mode 100644 index 00000000000..4b7dcc667fd --- /dev/null +++ b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/mixins/BindParameterMixin.kt @@ -0,0 +1,11 @@ +package app.cash.sqldelight.dialects.mysql.grammar.mixins + +import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin +import com.intellij.lang.ASTNode + +abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { + override fun replaceWith(isAsync: Boolean, index: Int): String = when (text) { + "DEFAULT" -> text + else -> "?" + } +} diff --git a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt index 4598e8194e9..151dddb0de4 100644 --- a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt +++ b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt @@ -4,5 +4,9 @@ import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin import com.intellij.lang.ASTNode abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { - override val replaceWith: String = if (text == "DEFAULT") text else "?" + override fun replaceWith(isAsync: Boolean, index: Int): String = when { + text == "DEFAULT" -> text + isAsync -> "$$index" + else -> "?" + } } diff --git a/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt b/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt index 7461ee48bf1..ec7aa13969d 100644 --- a/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt +++ b/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt @@ -11,5 +11,5 @@ abstract class BindParameterMixin(node: ASTNode) : SqlCompositeElementImpl(node) * Some sql dialects support other bind parameter besides `?`, but sqldelight should still replace the * user provided parameter with [replaceWith] for a homogen generated code. */ - open val replaceWith: String = "?" + open fun replaceWith(isAsync: Boolean, index: Int): String = "?" } diff --git a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt index c5fe62ddaf5..08ad4786b9b 100644 --- a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt +++ b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt @@ -206,7 +206,7 @@ abstract class QueryGenerator( // This allows us to use the same algorithm for non Sqlite dialects // :name becomes ? if (bindParameter != null) { - replacements.add(bindArg.range to bindParameter.replaceWith) + replacements.add(bindArg.range to bindParameter.replaceWith(generateAsync, index = nonArrayBindArgsCount)) } } } diff --git a/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt b/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt index 0ea5af6bffa..a66ec9427f0 100644 --- a/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt +++ b/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt @@ -16,6 +16,15 @@ class DialectIntegrationTests { Truth.assertThat(result.output).contains("BUILD SUCCESSFUL") } + @Test fun integrationTestsMySqlAsync() { + val runner = GradleRunner.create() + .withCommonConfiguration(File("src/test/integration-mysql-async")) + .withArguments("clean", "check", "--stacktrace") + + val result = runner.build() + Truth.assertThat(result.output).contains("BUILD SUCCESSFUL") + } + @Test fun integrationTestsMySqlSchemaDefinitions() { val runner = GradleRunner.create() .withCommonConfiguration(File("src/test/integration-mysql-schema")) diff --git a/sqldelight-gradle-plugin/src/test/integration-mysql-async/build.gradle b/sqldelight-gradle-plugin/src/test/integration-mysql-async/build.gradle index f50a9ed2829..8b1f9614cb1 100644 --- a/sqldelight-gradle-plugin/src/test/integration-mysql-async/build.gradle +++ b/sqldelight-gradle-plugin/src/test/integration-mysql-async/build.gradle @@ -7,7 +7,7 @@ apply plugin: 'app.cash.sqldelight' sqldelight { MyDatabase { - packageName = "app.cash.sqldelight.mysql.integration" + packageName = "app.cash.sqldelight.mysql.integration.async" dialect("app.cash.sqldelight:mysql-dialect:${app.cash.sqldelight.VersionKt.VERSION}") generateAsync = true } @@ -26,7 +26,7 @@ dependencies { implementation "org.testcontainers:r2dbc:1.16.2" implementation "dev.miku:r2dbc-mysql:0.8.2.RELEASE" implementation "app.cash.sqldelight:r2dbc-driver:${app.cash.sqldelight.VersionKt.VERSION}" - implementation "app.cash.sqldelight:coroutines-extensions:${app.cash.sqldelight.VersionKt.VERSION}" + implementation "app.cash.sqldelight:async-extensions:${app.cash.sqldelight.VersionKt.VERSION}" implementation libs.truth implementation libs.kotlin.coroutines.core implementation libs.kotlin.coroutines.test diff --git a/sqldelight-gradle-plugin/src/test/integration-mysql-async/settings.gradle b/sqldelight-gradle-plugin/src/test/integration-mysql-async/settings.gradle index 5b846acd802..4995799c151 100644 --- a/sqldelight-gradle-plugin/src/test/integration-mysql-async/settings.gradle +++ b/sqldelight-gradle-plugin/src/test/integration-mysql-async/settings.gradle @@ -1,3 +1,3 @@ apply from: "../settings.gradle" -rootProject.name = 'sqldelight-mysql-integration' +rootProject.name = 'sqldelight-mysql-integration-async' diff --git a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dates.sq b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dates.sq similarity index 100% rename from sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dates.sq rename to sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dates.sq diff --git a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dog.sq b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dog.sq similarity index 100% rename from sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dog.sq rename to sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dog.sq diff --git a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/MySqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/async/MySqlTest.kt similarity index 79% rename from sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/MySqlTest.kt rename to sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/async/MySqlTest.kt index 259142b9261..56fa6b1ff29 100644 --- a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/MySqlTest.kt +++ b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/async/MySqlTest.kt @@ -1,5 +1,8 @@ -package app.cash.sqldelight.mysql.integration +package app.cash.sqldelight.mysql.integration.async +import app.cash.sqldelight.async.coroutines.awaitAsList +import app.cash.sqldelight.async.coroutines.awaitAsOne +import app.cash.sqldelight.async.coroutines.awaitCreate import app.cash.sqldelight.driver.r2dbc.R2dbcDriver import com.google.common.truth.Truth.assertThat import io.r2dbc.spi.ConnectionFactories @@ -13,13 +16,13 @@ class MySqlTest { val connection = factory.create().awaitSingle() val driver = R2dbcDriver(connection) - val db = MyDatabase(driver).also { MyDatabase.Schema.create(driver) } + val db = MyDatabase(driver).also { MyDatabase.Schema.awaitCreate(driver) } block(db) } @Test fun simpleSelect() = runTest { database -> database.dogQueries.insertDog("Tilda", "Pomeranian", true) - assertThat(database.dogQueries.selectDogs().executeAsOne()) + assertThat(database.dogQueries.selectDogs().awaitAsOne()) .isEqualTo( Dog( name = "Tilda", @@ -40,7 +43,7 @@ class MySqlTest { dogQueries.selectDogsByBreedAndNames( breed = "Pomeranian", name = listOf("Tilda", "Buddy"), - ).executeAsList(), + ).awaitAsList(), ) .containsExactly( Dog( From bf605c041a011052c984fc3996edb05567cf4ef3 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Thu, 22 Sep 2022 18:51:11 +0200 Subject: [PATCH 05/19] Add postgresql async test --- .../sqldelight/driver/r2dbc/R2dbcDriver.kt | 21 +-- .../async/coroutines/QueryExtensions.kt | 8 +- .../core/compiler/QueryGenerator.kt | 2 +- .../dialect/DialectIntegrationTests.kt | 9 + .../integration-postgresql-async/build.gradle | 33 ++++ .../settings.gradle | 3 + .../postgresql/integration/async/Arrays.sq | 9 + .../postgresql/integration/async/Dates.sq | 17 ++ .../postgresql/integration/async/Dog.sq | 23 +++ .../postgresql/integration/async/OneEntity.sq | 14 ++ .../postgresql/integration/async/WithLock.sq | 25 +++ .../postgresql/integration/PostgreSqlTest.kt | 162 ++++++++++++++++++ 12 files changed, 311 insertions(+), 15 deletions(-) create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/build.gradle create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/settings.gradle create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Arrays.sq create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dates.sq create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dog.sq create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/OneEntity.sq create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/WithLock.sq create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt diff --git a/drivers/r2dbc-driver/src/main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt b/drivers/r2dbc-driver/src/main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt index 848110c617b..346cac32c0c 100644 --- a/drivers/r2dbc-driver/src/main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt +++ b/drivers/r2dbc-driver/src/main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt @@ -8,7 +8,9 @@ import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlPreparedStatement import io.r2dbc.spi.Connection import io.r2dbc.spi.Statement -import kotlinx.coroutines.reactive.awaitLast +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.reactive.asFlow +import kotlinx.coroutines.reactive.awaitFirstOrNull import kotlinx.coroutines.reactive.awaitSingle class R2dbcDriver(private val connection: Connection) : SqlDriver { @@ -26,10 +28,9 @@ class R2dbcDriver(private val connection: Connection) : SqlDriver { return QueryResult.AsyncValue { val result = prepared.execute().awaitSingle() - val rowSet = mutableListOf>() - result.map { row, rowMetadata -> - rowSet.add(rowMetadata.columnMetadatas.mapIndexed { index, _ -> index to row.get(index) }.toMap()) - }.awaitLast() + val rowSet = result.map { row, rowMetadata -> + List(rowMetadata.columnMetadatas.size) { index -> row.get(index) } + }.asFlow().toList() return@AsyncValue mapper(R2dbcCursor(rowSet)) } @@ -47,7 +48,7 @@ class R2dbcDriver(private val connection: Connection) : SqlDriver { return QueryResult.AsyncValue { val result = prepared.execute().awaitSingle() - return@AsyncValue result.rowsUpdated.awaitSingle() + return@AsyncValue result.rowsUpdated.awaitFirstOrNull() ?: 0 } } @@ -64,7 +65,7 @@ class R2dbcDriver(private val connection: Connection) : SqlDriver { this.transaction = transaction if (enclosing == null) { - connection.beginTransaction().awaitSingle() + connection.beginTransaction().awaitFirstOrNull() } return@AsyncValue transaction @@ -88,9 +89,9 @@ class R2dbcDriver(private val connection: Connection) : SqlDriver { override fun endTransaction(successful: Boolean): QueryResult = QueryResult.AsyncValue { if (enclosingTransaction == null) { if (successful) { - connection.commitTransaction().awaitSingle() + connection.commitTransaction().awaitFirstOrNull() } else { - connection.rollbackTransaction().awaitSingle() + connection.rollbackTransaction().awaitFirstOrNull() } } transaction = enclosingTransaction @@ -151,7 +152,7 @@ class R2dbcPreparedStatement(private val statement: Statement) : SqlPreparedStat /** * TODO: Write a better async cursor API */ -class R2dbcCursor(val rowSet: List>) : SqlCursor { +class R2dbcCursor(val rowSet: List>) : SqlCursor { var row = -1 private set diff --git a/extensions/async-extensions/src/commonMain/kotlin/app/cash/sqldelight/async/coroutines/QueryExtensions.kt b/extensions/async-extensions/src/commonMain/kotlin/app/cash/sqldelight/async/coroutines/QueryExtensions.kt index 17bb4ea359b..52808bc141a 100644 --- a/extensions/async-extensions/src/commonMain/kotlin/app/cash/sqldelight/async/coroutines/QueryExtensions.kt +++ b/extensions/async-extensions/src/commonMain/kotlin/app/cash/sqldelight/async/coroutines/QueryExtensions.kt @@ -1,19 +1,19 @@ package app.cash.sqldelight.async.coroutines -import app.cash.sqldelight.Query +import app.cash.sqldelight.ExecutableQuery -suspend fun Query.awaitAsList(): List = execute { cursor -> +suspend fun ExecutableQuery.awaitAsList(): List = execute { cursor -> val result = mutableListOf() while (cursor.next()) result.add(mapper(cursor)) result }.await() -suspend fun Query.awaitAsOne(): T { +suspend fun ExecutableQuery.awaitAsOne(): T { return awaitAsOneOrNull() ?: throw NullPointerException("ResultSet returned null for $this") } -suspend fun Query.awaitAsOneOrNull(): T? = execute { cursor -> +suspend fun ExecutableQuery.awaitAsOneOrNull(): T? = execute { cursor -> if (!cursor.next()) return@execute null val value = mapper(cursor) check(!cursor.next()) { "ResultSet returned more than 1 row for $this" } diff --git a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt index 08ad4786b9b..3702e388755 100644 --- a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt +++ b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt @@ -297,7 +297,7 @@ abstract class QueryGenerator( """ if (result%L == 0L) throw %T(%S) """.trimIndent(), - if (generateAsync) ".await()" else ".value", + if (generateAsync) "" else ".value", ClassName("app.cash.sqldelight.db", "OptimisticLockException"), "UPDATE on ${query.tablesAffected.single().name} failed because optimistic lock ${optimisticLock.name} did not match", ) diff --git a/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt b/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt index a66ec9427f0..256921c818d 100644 --- a/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt +++ b/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt @@ -43,6 +43,15 @@ class DialectIntegrationTests { Truth.assertThat(result.output).contains("BUILD SUCCESSFUL") } + @Test fun integrationTestsPostgreSqlAsync() { + val runner = GradleRunner.create() + .withCommonConfiguration(File("src/test/integration-postgresql-async")) + .withArguments("clean", "check", "--stacktrace") + + val result = runner.build() + Truth.assertThat(result.output).contains("BUILD SUCCESSFUL") + } + @Test fun `dialect accepts version catalog dependency`() { val runner = GradleRunner.create() .withCommonConfiguration(File("src/test/integration-catalog")) diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/build.gradle b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/build.gradle new file mode 100644 index 00000000000..601620d8c43 --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/build.gradle @@ -0,0 +1,33 @@ +buildscript { + apply from: "${projectDir.absolutePath}/../buildscript.gradle" +} + +apply plugin: 'org.jetbrains.kotlin.jvm' +apply plugin: 'app.cash.sqldelight' + +sqldelight { + MyDatabase { + packageName = "app.cash.sqldelight.postgresql.integration.async" + dialect("app.cash.sqldelight:postgresql-dialect:${app.cash.sqldelight.VersionKt.VERSION}") + generateAsync = true + } +} + +repositories { + maven { + url "file://${projectDir.absolutePath}/../../../../build/localMaven" + } + mavenCentral() +} + +dependencies { + implementation libs.postgresJdbc + implementation "org.testcontainers:postgresql:1.16.2" + implementation "org.testcontainers:r2dbc:1.16.2" + implementation "org.postgresql:r2dbc-postgresql:0.9.2.RELEASE" + implementation "app.cash.sqldelight:r2dbc-driver:${app.cash.sqldelight.VersionKt.VERSION}" + implementation "app.cash.sqldelight:async-extensions:${app.cash.sqldelight.VersionKt.VERSION}" + implementation libs.truth + implementation libs.kotlin.coroutines.test + implementation libs.kotlin.coroutines.reactive +} diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/settings.gradle b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/settings.gradle new file mode 100644 index 00000000000..2af80808ac6 --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/settings.gradle @@ -0,0 +1,3 @@ +apply from: "../settings.gradle" + +rootProject.name = 'sqldelight-postgresql-integration-async' diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Arrays.sq b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Arrays.sq new file mode 100644 index 00000000000..d4a440d0ab0 --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Arrays.sq @@ -0,0 +1,9 @@ +CREATE TABLE arrays( + intArray INTEGER[], + textArray TEXT[] +); + +insertAndReturn: +INSERT INTO arrays +VALUES (?, ?) +RETURNING *; \ No newline at end of file diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dates.sq b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dates.sq new file mode 100644 index 00000000000..115883395cf --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dates.sq @@ -0,0 +1,17 @@ +CREATE TABLE dates( + date DATE NOT NULL, + time TIME NOT NULL, + timestamp TIMESTAMP NOT NULL, + timestamp_with_timezone TIMESTAMP WITH TIME ZONE NOT NULL +); + +insertDate: +INSERT INTO dates +VALUES (?, ?, ?, ?) +RETURNING *; + +selectDateTrunc: +SELECT date_trunc('hour', timestamp), date_trunc('hour', timestamp_with_timezone) FROM dates; + +selectNow: +SELECT NOW(); diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dog.sq b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dog.sq new file mode 100644 index 00000000000..138341ababe --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dog.sq @@ -0,0 +1,23 @@ +CREATE TABLE dog ( + name VARCHAR(8) NOT NULL, + breed TEXT NOT NULL, + is_good INTEGER NOT NULL DEFAULT 1 +); + +insertDog: +INSERT INTO dog +VALUES (?, ?, DEFAULT); + +selectDogs: +SELECT * +FROM dog; + +selectGoodDogs: +SELECT * +FROM dog +WHERE :someBoolean AND 1 = 1; + +insertAndReturn: +INSERT INTO dog +VALUES (?, ?, DEFAULT) +RETURNING *; diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/OneEntity.sq b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/OneEntity.sq new file mode 100644 index 00000000000..8bad31ffcad --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/OneEntity.sq @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS oneEntity ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL +); + +insert: +INSERT INTO oneEntity(name) +VALUES (?); + +selectAll: +SELECT * FROM oneEntity; + +insertUpdate: +INSERT INTO oneEntity AS one VALUES ? ON CONFLICT(id) DO UPDATE SET id = one.id + EXCLUDED.id, name = EXCLUDED.name; diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/WithLock.sq b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/WithLock.sq new file mode 100644 index 00000000000..bc1648ec9a8 --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/WithLock.sq @@ -0,0 +1,25 @@ +CREATE TABLE withLock( + id SERIAL AS VALUE NOT NULL, + version INTEGER AS LOCK NOT NULL DEFAULT 0, + text TEXT NOT NULL +); + +insertText: +INSERT INTO withLock (text) +VALUES (?) +RETURNING *; + +updateText: +UPDATE withLock +SET + text = :text, + version = :version + 1 +WHERE + id = :id AND + version = :version +; + +selectForId: +SELECT * +FROM withLock +WHERE id = :id; \ No newline at end of file diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt new file mode 100644 index 00000000000..fd71b2692a5 --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt @@ -0,0 +1,162 @@ +package app.cash.sqldelight.postgresql.integration + +import app.cash.sqldelight.async.coroutines.* +import app.cash.sqldelight.db.OptimisticLockException +import app.cash.sqldelight.driver.r2dbc.* +import app.cash.sqldelight.postgresql.integration.async.* +import com.google.common.truth.Truth.assertThat +import io.r2dbc.spi.* +import kotlinx.coroutines.reactive.* +import org.junit.Assert +import org.junit.Test +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.OffsetDateTime +import java.time.ZoneOffset + +class PostgreSqlTest { + private val factory = ConnectionFactories.get("r2dbc:tc:postgresql:///myDb?TC_IMAGE_TAG=9.6.8") + + private fun runTest(block: suspend (MyDatabase) -> Unit) = kotlinx.coroutines.test.runTest { + val connection = factory.create().awaitSingle() + val driver = R2dbcDriver(connection) + val db = MyDatabase(driver) + MyDatabase.Schema.awaitCreate(driver) + block(db) + } + + @Test fun simpleSelect() = runTest { database -> + Assert.assertEquals(0, database.dogQueries.selectDogs().awaitAsList().size) + database.dogQueries.insertDog("Tilda", "Pomeranian") + assertThat(database.dogQueries.selectDogs().awaitAsOne()) + .isEqualTo( + Dog( + name = "Tilda", + breed = "Pomeranian", + is_good = 1, + ), + ) + } + + @Test fun booleanSelect() = runTest { database -> + database.dogQueries.insertDog("Tilda", "Pomeranian") + assertThat(database.dogQueries.selectGoodDogs(true).awaitAsOne()) + .isEqualTo( + Dog( + name = "Tilda", + breed = "Pomeranian", + is_good = 1, + ), + ) + } + + @Test fun returningInsert() = runTest { database -> + assertThat(database.dogQueries.insertAndReturn("Tilda", "Pomeranian").awaitAsOne()) + .isEqualTo( + Dog( + name = "Tilda", + breed = "Pomeranian", + is_good = 1, + ), + ) + } + + @Test fun testDates() = runTest { database -> + assertThat( + database.datesQueries.insertDate( + date = LocalDate.of(2020, 1, 1), + time = LocalTime.of(21, 30, 59, 10000), + timestamp = LocalDateTime.of(2020, 1, 1, 21, 30, 59, 10000), + timestamp_with_timezone = OffsetDateTime.of(1980, 4, 9, 20, 15, 45, 0, ZoneOffset.ofHours(0)), + ).awaitAsOne() + , + ) + .isEqualTo( + Dates( + date = LocalDate.of(2020, 1, 1), + time = LocalTime.of(21, 30, 59, 10000), + timestamp = LocalDateTime.of(2020, 1, 1, 21, 30, 59, 10000), + timestamp_with_timezone = OffsetDateTime.of(1980, 4, 9, 20, 15, 45, 0, ZoneOffset.ofHours(0)), + ), + ) + } + + @Test fun testDateTrunc() = runTest { database -> + database.datesQueries.insertDate( + date = LocalDate.of(2020, 1, 1), + time = LocalTime.of(21, 30, 59, 10000), + timestamp = LocalDateTime.of(2020, 1, 1, 21, 30, 59, 10000), + timestamp_with_timezone = OffsetDateTime.of(1980, 4, 9, 20, 15, 45, 0, ZoneOffset.ofHours(0)), + ).awaitAsOne() + + assertThat( + database.datesQueries.selectDateTrunc().awaitAsOne(), + ) + .isEqualTo( + SelectDateTrunc( + date_trunc = LocalDateTime.of(2020, 1, 1, 21, 0, 0, 0), + date_trunc_ = OffsetDateTime.of(1980, 4, 9, 20, 0, 0, 0, ZoneOffset.ofHours(0)), + ), + ) + } + + @Test fun testSerial() = runTest { database -> + database.run { + oneEntityQueries.transaction { + oneEntityQueries.insert("name1") + oneEntityQueries.insert("name2") + oneEntityQueries.insert("name3") + } + assertThat(oneEntityQueries.selectAll().awaitAsList().map { it.id }).containsExactly(1, 2, 3) + } + } + + @Test fun testArrays() = runTest { database -> + with(database.arraysQueries.insertAndReturn(arrayOf(1, 2), arrayOf("one", "two")).awaitAsOne()) { + assertThat(intArray!!.asList()).containsExactly(1, 2).inOrder() + assertThat(textArray!!.asList()).containsExactly("one", "two").inOrder() + } + } + + @Test fun now() = runTest { database -> + val now = database.datesQueries.selectNow().awaitAsOne() + assertThat(now).isNotNull() + assertThat(now).isGreaterThan(OffsetDateTime.MIN) + } + + @Test fun successfulOptimisticLock() = runTest { database -> + with(database.withLockQueries) { + val row = insertText("sup").awaitAsOne() + + updateText( + id = row.id, + version = row.version, + text = "sup2", + ) + + assertThat(selectForId(row.id).awaitAsOne().text).isEqualTo("sup2") + } + } + + @Test fun unsuccessfulOptimisticLock() = runTest { database -> + with(database.withLockQueries) { + val row = insertText("sup").awaitAsOne() + + updateText( + id = row.id, + version = row.version, + text = "sup2", + ) + + try { + updateText( + id = row.id, + version = row.version, + text = "sup3", + ) + Assert.fail() + } catch (e: OptimisticLockException) { } + } + } +} From b583718b76a1e4a30a9fc1888251a5da9f0bcf61 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Thu, 22 Sep 2022 19:02:15 +0200 Subject: [PATCH 06/19] Fix wildards --- .../integration/{ => async}/PostgreSqlTest.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) rename sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/{ => async}/PostgreSqlTest.kt (92%) diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/async/PostgreSqlTest.kt similarity index 92% rename from sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt rename to sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/async/PostgreSqlTest.kt index fd71b2692a5..960db5deb21 100644 --- a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/async/PostgreSqlTest.kt @@ -1,12 +1,13 @@ -package app.cash.sqldelight.postgresql.integration +package app.cash.sqldelight.postgresql.integration.async -import app.cash.sqldelight.async.coroutines.* +import app.cash.sqldelight.async.coroutines.awaitAsList +import app.cash.sqldelight.async.coroutines.awaitAsOne +import app.cash.sqldelight.async.coroutines.awaitCreate import app.cash.sqldelight.db.OptimisticLockException -import app.cash.sqldelight.driver.r2dbc.* -import app.cash.sqldelight.postgresql.integration.async.* +import app.cash.sqldelight.driver.r2dbc.R2dbcDriver import com.google.common.truth.Truth.assertThat -import io.r2dbc.spi.* -import kotlinx.coroutines.reactive.* +import io.r2dbc.spi.ConnectionFactories +import kotlinx.coroutines.reactive.awaitSingle import org.junit.Assert import org.junit.Test import java.time.LocalDate From bf3de3864444668ed9bb0044a07e073828f2e7b7 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Thu, 22 Sep 2022 19:08:13 +0200 Subject: [PATCH 07/19] Fix code style --- .../sqldelight/postgresql/integration/async/PostgreSqlTest.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/async/PostgreSqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/async/PostgreSqlTest.kt index 960db5deb21..2d90cebfd70 100644 --- a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/async/PostgreSqlTest.kt +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/async/PostgreSqlTest.kt @@ -70,8 +70,7 @@ class PostgreSqlTest { time = LocalTime.of(21, 30, 59, 10000), timestamp = LocalDateTime.of(2020, 1, 1, 21, 30, 59, 10000), timestamp_with_timezone = OffsetDateTime.of(1980, 4, 9, 20, 15, 45, 0, ZoneOffset.ofHours(0)), - ).awaitAsOne() - , + ).awaitAsOne(), ) .isEqualTo( Dates( From b2593f7a4092f934e5a7e5a9a131ce9a3e2eb050 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Fri, 23 Sep 2022 08:11:00 +0200 Subject: [PATCH 08/19] Add default to mysql too --- .../app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf | 2 +- .../cash/sqldelight/dialects/mysql/grammar/MySql.bnf | 2 +- .../dialects/postgresql/grammar/PostgreSql.bnf | 2 +- .../app/cash/sqldelight/mysql/integration/async/Dog.sq | 2 +- .../sqldelight/mysql/integration/async/MySqlTest.kt | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf index b4f77a56edf..12815f8af31 100644 --- a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf +++ b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf @@ -79,7 +79,7 @@ column_constraint ::= [ CONSTRAINT {identifier} ] ( implements = "com.alecstrong.sql.psi.core.psi.SqlColumnConstraint" override = true } -bind_parameter ::= ( DEFAULT | '?' | ':' {identifier} ) { +bind_parameter ::= DEFAULT | ( '?' | ':' {identifier} ) { mixin = "app.cash.sqldelight.dialects.hsql.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" diff --git a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf index a9759bf100c..8ef7a5c7956 100644 --- a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf +++ b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf @@ -111,7 +111,7 @@ column_constraint ::= [ CONSTRAINT {identifier} ] ( implements = "com.alecstrong.sql.psi.core.psi.SqlColumnConstraint" override = true } -bind_parameter ::= ( '?' | ':' {identifier} ) { +bind_parameter ::= DEFAULT ( '?' | ':' {identifier} ) { mixin = "app.cash.sqldelight.dialects.mysql.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" diff --git a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf index dd471ea1754..9d6e5f64c5c 100644 --- a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf +++ b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf @@ -110,7 +110,7 @@ type_name ::= ( implements = "com.alecstrong.sql.psi.core.psi.SqlTypeName" override = true } -bind_parameter ::= ( DEFAULT | '?' | ':' {identifier} ) { +bind_parameter ::= DEFAULT | ( '?' | ':' {identifier} ) { mixin = "app.cash.sqldelight.dialects.postgresql.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" diff --git a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dog.sq b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dog.sq index 977e509000b..5ffd3d40620 100644 --- a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dog.sq +++ b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dog.sq @@ -6,7 +6,7 @@ CREATE TABLE dog ( insertDog: INSERT INTO dog -VALUES (?, ?, ?); +VALUES (?, ?, DEFAULT); selectDogs: SELECT * diff --git a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/async/MySqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/async/MySqlTest.kt index 56fa6b1ff29..ec657a6af6b 100644 --- a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/async/MySqlTest.kt +++ b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/async/MySqlTest.kt @@ -21,7 +21,7 @@ class MySqlTest { } @Test fun simpleSelect() = runTest { database -> - database.dogQueries.insertDog("Tilda", "Pomeranian", true) + database.dogQueries.insertDog("Tilda", "Pomeranian") assertThat(database.dogQueries.selectDogs().awaitAsOne()) .isEqualTo( Dog( @@ -35,10 +35,10 @@ class MySqlTest { @Test fun simpleSelectWithIn() = runTest { database -> with(database) { - dogQueries.insertDog("Tilda", "Pomeranian", true) - dogQueries.insertDog("Tucker", "Portuguese Water Dog", true) - dogQueries.insertDog("Cujo", "Pomeranian", false) - dogQueries.insertDog("Buddy", "Pomeranian", true) + dogQueries.insertDog("Tilda", "Pomeranian") + dogQueries.insertDog("Tucker", "Portuguese Water Dog") + dogQueries.insertDog("Cujo", "Pomeranian") + dogQueries.insertDog("Buddy", "Pomeranian") assertThat( dogQueries.selectDogsByBreedAndNames( breed = "Pomeranian", From f5619f6f9849c12cea7aeada14f4f93cbc642706 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Fri, 23 Sep 2022 08:20:31 +0200 Subject: [PATCH 09/19] Downgrade r2dbc spi due binary incompatible with drivers Fix docker test dependencies --- .../kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf | 2 +- .../main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt | 2 +- gradle/libs.versions.toml | 2 +- sqldelight-gradle-plugin/build.gradle | 2 ++ 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf index 8ef7a5c7956..e91c30d3112 100644 --- a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf +++ b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf @@ -111,7 +111,7 @@ column_constraint ::= [ CONSTRAINT {identifier} ] ( implements = "com.alecstrong.sql.psi.core.psi.SqlColumnConstraint" override = true } -bind_parameter ::= DEFAULT ( '?' | ':' {identifier} ) { +bind_parameter ::= DEFAULT | ( '?' | ':' {identifier} ) { mixin = "app.cash.sqldelight.dialects.mysql.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" diff --git a/drivers/r2dbc-driver/src/main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt b/drivers/r2dbc-driver/src/main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt index 346cac32c0c..2e8013f9561 100644 --- a/drivers/r2dbc-driver/src/main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt +++ b/drivers/r2dbc-driver/src/main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt @@ -48,7 +48,7 @@ class R2dbcDriver(private val connection: Connection) : SqlDriver { return QueryResult.AsyncValue { val result = prepared.execute().awaitSingle() - return@AsyncValue result.rowsUpdated.awaitFirstOrNull() ?: 0 + return@AsyncValue result.rowsUpdated.awaitFirstOrNull()?.toLong() ?: 0 } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5439bf623bc..087367d2350 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -76,7 +76,7 @@ testhelp = { module = "co.touchlab:testhelp", version.ref = "testhelp" } burst = { module = "com.squareup.burst:burst-junit4", version = "1.2.0" } testParameterInjector = { module = "com.google.testparameterinjector:test-parameter-injector", version = "1.8" } -r2dbc = { module = "io.r2dbc:r2dbc-spi", version = "1.0.0.RELEASE" } +r2dbc = { module = "io.r2dbc:r2dbc-spi", version = "0.9.1.RELEASE" } [plugins] android-library = { id = "com.android.library", version.ref = "agp" } diff --git a/sqldelight-gradle-plugin/build.gradle b/sqldelight-gradle-plugin/build.gradle index 39817f16663..8d7001c6a19 100644 --- a/sqldelight-gradle-plugin/build.gradle +++ b/sqldelight-gradle-plugin/build.gradle @@ -124,6 +124,8 @@ tasks.dockerTest.configure { ":sqlite-migrations:publishAllPublicationsToInstallLocallyRepository", ":sqldelight-compiler:publishAllPublicationsToInstallLocallyRepository", ":sqldelight-gradle-plugin:publishAllPublicationsToInstallLocallyRepository", + ":drivers:r2dbc-driver:publishAllPublicationsToInstallLocallyRepository", + ":extensions:async-extensions:publishAllPublicationsToInstallLocallyRepository", ) } From 466a934fc79f2e5d027f9fbb92bd92a6ec41e600 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Fri, 15 Jul 2022 17:34:31 +0200 Subject: [PATCH 10/19] Fix DEFAULT in binding Add test for PostgreSQL Spotless Move mixin to sqldelight Use mixin instead Don't add DEFAULT bindings to query Fix DEFAULT in binding --- .../sqldelight/dialects/hsql/grammar/hsql.bnf | 1 + .../hsql/grammar/mixins/BindParameterMixin.kt | 9 +++ .../dialects/mysql/grammar/MySql.bnf | 1 + .../postgresql/grammar/PostgreSql.bnf | 3 +- .../grammar/mixins/BindParameterMixin.kt | 9 +++ .../postgres/PostgreSqlFixturesTest.kt | 1 + .../dialects/sqlite_3_18/grammar/sqlite.bnf | 1 + .../grammar/mixins/BindParameterMixin.kt | 23 ++++++++ .../core/compiler/QueryGenerator.kt | 56 ++++++++++--------- .../core/compiler/model/BindableQuery.kt | 36 ++++++------ .../cash/sqldelight/hsql/integration/Dog.sq | 2 +- .../sqldelight/hsql/integration/HsqlTest.kt | 2 +- .../sqldelight/postgresql/integration/Dog.sq | 6 +- .../postgresql/integration/PostgreSqlTest.kt | 6 +- 14 files changed, 105 insertions(+), 51 deletions(-) create mode 100644 dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt create mode 100644 dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt create mode 100644 sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt diff --git a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf index 6cce18d0de3..b4f77a56edf 100644 --- a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf +++ b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf @@ -80,6 +80,7 @@ column_constraint ::= [ CONSTRAINT {identifier} ] ( override = true } bind_parameter ::= ( DEFAULT | '?' | ':' {identifier} ) { + mixin = "app.cash.sqldelight.dialects.hsql.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" override = true diff --git a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt new file mode 100644 index 00000000000..ed59bfc1fe9 --- /dev/null +++ b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt @@ -0,0 +1,9 @@ +package app.cash.sqldelight.dialects.hsql.grammar.mixins + +import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin +import com.intellij.lang.ASTNode + +abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { + override val replaceWith: String = if (text == "DEFAULT") text else "?" + override val isDefault = text == "DEFAULT" +} diff --git a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf index 9230ea0ec3d..59e5472b200 100644 --- a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf +++ b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf @@ -112,6 +112,7 @@ column_constraint ::= [ CONSTRAINT {identifier} ] ( override = true } bind_parameter ::= ( '?' | ':' {identifier} ) { + mixin = "app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" override = true diff --git a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf index 50f85881755..d2c77428d89 100644 --- a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf +++ b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf @@ -111,6 +111,7 @@ type_name ::= ( override = true } bind_parameter ::= ( DEFAULT | '?' | ':' {identifier} ) { + mixin = "app.cash.sqldelight.dialects.postgresql.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" override = true @@ -118,7 +119,7 @@ bind_parameter ::= ( DEFAULT | '?' | ':' {identifier} ) { identity_clause ::= 'IDENTITY' -generated_clause ::= GENERATED ( (ALWAYS AS <> 'STORED') | ( (ALWAYS | BY DEFAULT) AS identity_clause ) ) { +generated_clause ::= GENERATED ( (ALWAYS AS '(' <> ')' 'STORED') | ( (ALWAYS | BY DEFAULT) AS identity_clause ) ) { extends = "com.alecstrong.sql.psi.core.psi.impl.SqlGeneratedClauseImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlGeneratedClause" override = true diff --git a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt new file mode 100644 index 00000000000..2381cef92ad --- /dev/null +++ b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt @@ -0,0 +1,9 @@ +package app.cash.sqldelight.dialects.postgresql.grammar.mixins + +import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin +import com.intellij.lang.ASTNode + +abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { + override val replaceWith: String = if (text == "DEFAULT") text else "?" + override val isDefault = text == "DEFAULT" +} diff --git a/dialects/postgresql/src/test/kotlin/app/cash/sqldelight/dialects/postgres/PostgreSqlFixturesTest.kt b/dialects/postgresql/src/test/kotlin/app/cash/sqldelight/dialects/postgres/PostgreSqlFixturesTest.kt index 3443e8b9e90..1a332d2dd64 100644 --- a/dialects/postgresql/src/test/kotlin/app/cash/sqldelight/dialects/postgres/PostgreSqlFixturesTest.kt +++ b/dialects/postgresql/src/test/kotlin/app/cash/sqldelight/dialects/postgres/PostgreSqlFixturesTest.kt @@ -15,6 +15,7 @@ class PostgreSqlFixturesTest(name: String, fixtureRoot: File) : FixturesTest(nam "?1" to "?", "?2" to "?", "BLOB" to "TEXT", + "id TEXT GENERATED ALWAYS AS (2) UNIQUE NOT NULL" to "id TEXT GENERATED ALWAYS AS (2) STORED UNIQUE NOT NULL", ) override fun setupDialect() { diff --git a/dialects/sqlite-3-18/src/main/kotlin/app/cash/sqldelight/dialects/sqlite_3_18/grammar/sqlite.bnf b/dialects/sqlite-3-18/src/main/kotlin/app/cash/sqldelight/dialects/sqlite_3_18/grammar/sqlite.bnf index 2f0c664fd28..4297b957696 100644 --- a/dialects/sqlite-3-18/src/main/kotlin/app/cash/sqldelight/dialects/sqlite_3_18/grammar/sqlite.bnf +++ b/dialects/sqlite-3-18/src/main/kotlin/app/cash/sqldelight/dialects/sqlite_3_18/grammar/sqlite.bnf @@ -27,6 +27,7 @@ int_data_type ::= 'INTEGER' real_data_type ::= 'REAL' bind_parameter ::= ( '?' [digit] | ':' {identifier} ) { + mixin = "app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" override = true diff --git a/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt b/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt new file mode 100644 index 00000000000..bf2aef6ede2 --- /dev/null +++ b/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt @@ -0,0 +1,23 @@ +package app.cash.sqldelight.dialect.grammar.mixins + +import com.alecstrong.sql.psi.core.psi.SqlBindParameter +import com.alecstrong.sql.psi.core.psi.SqlCompositeElementImpl +import com.intellij.lang.ASTNode + +abstract class BindParameterMixin(node: ASTNode) : SqlCompositeElementImpl(node), SqlBindParameter { + /** + * Overwrite, if the user provided sql parameter should be overwritten by sqldelight with [replaceWith]. + * + * Some sql dialects support other bind parameter besides `?`, but sqldelight should still replace the + * user provided parameter with [replaceWith] for a homogen generated code. + */ + open val replaceWith: String = "?" + + /** + * If true, this parameter is not used by code generators during parameter mapping. + * + * Some sql dialects support generated and default columns, so sqldelight should skip this parameter to not + * require and map it from the given parameters. + */ + open val isDefault: Boolean = false +} diff --git a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt index c223709b2d5..10694aa3e39 100644 --- a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt +++ b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt @@ -19,6 +19,7 @@ import app.cash.sqldelight.core.lang.util.rawSqlText import app.cash.sqldelight.core.lang.util.sqFile import app.cash.sqldelight.core.psi.SqlDelightStmtClojureStmtList import app.cash.sqldelight.dialect.api.IntermediateType +import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin import com.alecstrong.sql.psi.core.psi.SqlBinaryEqualityExpr import com.alecstrong.sql.psi.core.psi.SqlBindExpr import com.alecstrong.sql.psi.core.psi.SqlStmt @@ -174,36 +175,39 @@ abstract class QueryGenerator( precedingArrays.add(type.name) argumentCounts.add("${type.name}.size") } else { - nonArrayBindArgsCount += 1 - - if (!treatNullAsUnknownForEquality && type.javaType.isNullable) { - val parent = bindArg?.parent - if (parent is SqlBinaryEqualityExpr) { - needsFreshStatement = true - - var symbol = parent.childOfType(SqlTypes.EQ) ?: parent.childOfType(SqlTypes.EQ2) - val nullableEquality: String - if (symbol != null) { - nullableEquality = "${symbol.leftWhitspace()}IS${symbol.rightWhitespace()}" - } else { - symbol = parent.childOfType(SqlTypes.NEQ) ?: parent.childOfType(SqlTypes.NEQ2)!! - nullableEquality = "${symbol.leftWhitspace()}IS NOT${symbol.rightWhitespace()}" + val bindParameter = bindArg?.bindParameter as? BindParameterMixin + if (bindParameter == null || !bindParameter.isDefault) { + nonArrayBindArgsCount += 1 + + if (!treatNullAsUnknownForEquality && type.javaType.isNullable) { + val parent = bindArg?.parent + if (parent is SqlBinaryEqualityExpr) { + needsFreshStatement = true + + var symbol = parent.childOfType(SqlTypes.EQ) ?: parent.childOfType(SqlTypes.EQ2) + val nullableEquality: String + if (symbol != null) { + nullableEquality = "${symbol.leftWhitspace()}IS${symbol.rightWhitespace()}" + } else { + symbol = parent.childOfType(SqlTypes.NEQ) ?: parent.childOfType(SqlTypes.NEQ2)!! + nullableEquality = "${symbol.leftWhitspace()}IS NOT${symbol.rightWhitespace()}" + } + + val block = CodeBlock.of("if (${type.name} == null) \"$nullableEquality\" else \"${symbol.text}\"") + replacements.add(symbol.range to "\${ $block }") } - - val block = CodeBlock.of("if (${type.name} == null) \"$nullableEquality\" else \"${symbol.text}\"") - replacements.add(symbol.range to "\${ $block }") } - } - // Binds each parameter to the statement: - // statement.bindLong(0, id) - bindStatements.add(type.preparedStatementBinder(offset, extractedVariables[type])) + // Binds each parameter to the statement: + // statement.bindLong(0, id) + bindStatements.add(type.preparedStatementBinder(offset, extractedVariables[type])) - // Replace the named argument with a non named/indexed argument. - // This allows us to use the same algorithm for non Sqlite dialects - // :name becomes ? - if (bindArg != null) { - replacements.add(bindArg.range to "?") + // Replace the named argument with a non named/indexed argument. + // This allows us to use the same algorithm for non Sqlite dialects + // :name becomes ? + if (bindParameter != null) { + replacements.add(bindArg.range to bindParameter.replaceWith) + } } } } diff --git a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt index b7de29ff05e..839209bbbcf 100644 --- a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt +++ b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt @@ -31,6 +31,7 @@ import app.cash.sqldelight.dialect.api.PrimitiveType.ARGUMENT import app.cash.sqldelight.dialect.api.PrimitiveType.BOOLEAN import app.cash.sqldelight.dialect.api.PrimitiveType.INTEGER import app.cash.sqldelight.dialect.api.PrimitiveType.NULL +import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin import com.alecstrong.sql.psi.core.psi.SqlAnnotatedElement import com.alecstrong.sql.psi.core.psi.SqlBindExpr import com.alecstrong.sql.psi.core.psi.SqlBindParameter @@ -91,29 +92,32 @@ abstract class BindableQuery( val namesSeen = mutableSetOf() var maxIndexSeen = 0 statement.findChildrenOfType().forEach { bindArg -> - bindArg.bindParameter.node.findChildByType(SqlTypes.DIGIT)?.text?.toInt()?.let { index -> - if (!indexesSeen.add(index)) { - result.findAndReplace(bindArg, index) { it.index == index } + val bindParameter = bindArg.bindParameter + if (bindParameter is BindParameterMixin && !bindParameter.isDefault) { + bindParameter.node.findChildByType(SqlTypes.DIGIT)?.text?.toInt()?.let { index -> + if (!indexesSeen.add(index)) { + result.findAndReplace(bindArg, index) { it.index == index } + return@forEach + } + maxIndexSeen = maxOf(maxIndexSeen, index) + result.add(Argument(index, typeResolver.argumentType(bindArg), mutableListOf(bindArg))) return@forEach } - maxIndexSeen = maxOf(maxIndexSeen, index) - result.add(Argument(index, typeResolver.argumentType(bindArg), mutableListOf(bindArg))) - return@forEach - } - bindArg.bindParameter.identifier?.let { - if (!namesSeen.add(it.text)) { - result.findAndReplace(bindArg) { (_, type, _) -> type.name == it.text } + bindParameter.identifier?.let { + if (!namesSeen.add(it.text)) { + result.findAndReplace(bindArg) { (_, type, _) -> type.name == it.text } + return@forEach + } + val index = ++maxIndexSeen + indexesSeen.add(index) + manuallyNamedIndexes.add(index) + result.add(Argument(index, typeResolver.argumentType(bindArg).copy(name = it.text), mutableListOf(bindArg))) return@forEach } val index = ++maxIndexSeen indexesSeen.add(index) - manuallyNamedIndexes.add(index) - result.add(Argument(index, typeResolver.argumentType(bindArg).copy(name = it.text), mutableListOf(bindArg))) - return@forEach + result.add(Argument(index, typeResolver.argumentType(bindArg), mutableListOf(bindArg))) } - val index = ++maxIndexSeen - indexesSeen.add(index) - result.add(Argument(index, typeResolver.argumentType(bindArg), mutableListOf(bindArg))) } // If there are still naming conflicts (edge case where the name we generate is the same as diff --git a/sqldelight-gradle-plugin/src/test/integration-hsql/src/main/sqldelight/app/cash/sqldelight/hsql/integration/Dog.sq b/sqldelight-gradle-plugin/src/test/integration-hsql/src/main/sqldelight/app/cash/sqldelight/hsql/integration/Dog.sq index f1d41860df4..e8a76594385 100644 --- a/sqldelight-gradle-plugin/src/test/integration-hsql/src/main/sqldelight/app/cash/sqldelight/hsql/integration/Dog.sq +++ b/sqldelight-gradle-plugin/src/test/integration-hsql/src/main/sqldelight/app/cash/sqldelight/hsql/integration/Dog.sq @@ -8,7 +8,7 @@ CREATE TABLE dog ( insertDog: INSERT INTO dog (name, breed, is_good, id) -VALUES (?, ?, ?, ?); +VALUES (?, ?, DEFAULT, ?); selectDogs: SELECT * diff --git a/sqldelight-gradle-plugin/src/test/integration-hsql/src/test/kotlin/app/cash/sqldelight/hsql/integration/HsqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-hsql/src/test/kotlin/app/cash/sqldelight/hsql/integration/HsqlTest.kt index 94cd092ee67..3068d964ad3 100644 --- a/sqldelight-gradle-plugin/src/test/integration-hsql/src/test/kotlin/app/cash/sqldelight/hsql/integration/HsqlTest.kt +++ b/sqldelight-gradle-plugin/src/test/integration-hsql/src/test/kotlin/app/cash/sqldelight/hsql/integration/HsqlTest.kt @@ -29,7 +29,7 @@ class HsqlTest { } @Test fun simpleSelect() { - database.dogQueries.insertDog("Tilda", "Pomeranian", true, 1) + database.dogQueries.insertDog("Tilda", "Pomeranian", 1) assertThat(database.dogQueries.selectDogs().executeAsOne()) .isEqualTo( Dog( diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/Dog.sq b/sqldelight-gradle-plugin/src/test/integration-postgresql/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/Dog.sq index 77c09eda934..138341ababe 100644 --- a/sqldelight-gradle-plugin/src/test/integration-postgresql/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/Dog.sq +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/Dog.sq @@ -6,7 +6,7 @@ CREATE TABLE dog ( insertDog: INSERT INTO dog -VALUES (?, ?, ?); +VALUES (?, ?, DEFAULT); selectDogs: SELECT * @@ -19,5 +19,5 @@ WHERE :someBoolean AND 1 = 1; insertAndReturn: INSERT INTO dog -VALUES (?, ?, ?) -RETURNING *; \ No newline at end of file +VALUES (?, ?, DEFAULT) +RETURNING *; diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-postgresql/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt index 28ebb8b8957..7c21fac9725 100644 --- a/sqldelight-gradle-plugin/src/test/integration-postgresql/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt @@ -36,7 +36,7 @@ class PostgreSqlTest { } @Test fun simpleSelect() { - database.dogQueries.insertDog("Tilda", "Pomeranian", 1) + database.dogQueries.insertDog("Tilda", "Pomeranian") assertThat(database.dogQueries.selectDogs().executeAsOne()) .isEqualTo( Dog( @@ -48,7 +48,7 @@ class PostgreSqlTest { } @Test fun booleanSelect() { - database.dogQueries.insertDog("Tilda", "Pomeranian", 1) + database.dogQueries.insertDog("Tilda", "Pomeranian") assertThat(database.dogQueries.selectGoodDogs(true).executeAsOne()) .isEqualTo( Dog( @@ -60,7 +60,7 @@ class PostgreSqlTest { } @Test fun returningInsert() { - assertThat(database.dogQueries.insertAndReturn("Tilda", "Pomeranian", 1).executeAsOne()) + assertThat(database.dogQueries.insertAndReturn("Tilda", "Pomeranian").executeAsOne()) .isEqualTo( Dog( name = "Tilda", From 053327ba08ff0d314147b48ca4cb35585a1bc1f3 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Thu, 22 Sep 2022 12:06:26 +0200 Subject: [PATCH 11/19] Remove isDefault api --- .../dialects/hsql/grammar/mixins/BindParameterMixin.kt | 1 - .../postgresql/grammar/mixins/BindParameterMixin.kt | 1 - .../dialect/grammar/mixins/BindParameterMixin.kt | 8 -------- .../app/cash/sqldelight/core/compiler/QueryGenerator.kt | 2 +- .../cash/sqldelight/core/compiler/model/BindableQuery.kt | 2 +- 5 files changed, 2 insertions(+), 12 deletions(-) diff --git a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt index ed59bfc1fe9..d94f1e65dd8 100644 --- a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt +++ b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt @@ -5,5 +5,4 @@ import com.intellij.lang.ASTNode abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { override val replaceWith: String = if (text == "DEFAULT") text else "?" - override val isDefault = text == "DEFAULT" } diff --git a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt index 2381cef92ad..4598e8194e9 100644 --- a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt +++ b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt @@ -5,5 +5,4 @@ import com.intellij.lang.ASTNode abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { override val replaceWith: String = if (text == "DEFAULT") text else "?" - override val isDefault = text == "DEFAULT" } diff --git a/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt b/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt index bf2aef6ede2..7461ee48bf1 100644 --- a/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt +++ b/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt @@ -12,12 +12,4 @@ abstract class BindParameterMixin(node: ASTNode) : SqlCompositeElementImpl(node) * user provided parameter with [replaceWith] for a homogen generated code. */ open val replaceWith: String = "?" - - /** - * If true, this parameter is not used by code generators during parameter mapping. - * - * Some sql dialects support generated and default columns, so sqldelight should skip this parameter to not - * require and map it from the given parameters. - */ - open val isDefault: Boolean = false } diff --git a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt index 10694aa3e39..c5fe62ddaf5 100644 --- a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt +++ b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt @@ -176,7 +176,7 @@ abstract class QueryGenerator( argumentCounts.add("${type.name}.size") } else { val bindParameter = bindArg?.bindParameter as? BindParameterMixin - if (bindParameter == null || !bindParameter.isDefault) { + if (bindParameter == null || bindParameter.text != "DEFAULT") { nonArrayBindArgsCount += 1 if (!treatNullAsUnknownForEquality && type.javaType.isNullable) { diff --git a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt index 839209bbbcf..afd5d7f10ef 100644 --- a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt +++ b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/model/BindableQuery.kt @@ -93,7 +93,7 @@ abstract class BindableQuery( var maxIndexSeen = 0 statement.findChildrenOfType().forEach { bindArg -> val bindParameter = bindArg.bindParameter - if (bindParameter is BindParameterMixin && !bindParameter.isDefault) { + if (bindParameter is BindParameterMixin && bindParameter.text != "DEFAULT") { bindParameter.node.findChildByType(SqlTypes.DIGIT)?.text?.toInt()?.let { index -> if (!indexesSeen.add(index)) { result.findAndReplace(bindArg, index) { it.index == index } From 19b3723925d36f52234e8afe958b5a53d14126a4 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Thu, 22 Sep 2022 12:11:37 +0200 Subject: [PATCH 12/19] Fix grammar --- .../cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf index d2c77428d89..dd471ea1754 100644 --- a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf +++ b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf @@ -119,7 +119,7 @@ bind_parameter ::= ( DEFAULT | '?' | ':' {identifier} ) { identity_clause ::= 'IDENTITY' -generated_clause ::= GENERATED ( (ALWAYS AS '(' <> ')' 'STORED') | ( (ALWAYS | BY DEFAULT) AS identity_clause ) ) { +generated_clause ::= GENERATED ( (ALWAYS AS LP <> RP 'STORED') | ( (ALWAYS | BY DEFAULT) AS identity_clause ) ) { extends = "com.alecstrong.sql.psi.core.psi.impl.SqlGeneratedClauseImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlGeneratedClause" override = true From c3169c1f3d815913c32d5569830772152a0d39ea Mon Sep 17 00:00:00 2001 From: hfhbd Date: Thu, 22 Sep 2022 15:54:47 +0200 Subject: [PATCH 13/19] Add mysql async test --- .../hsql/grammar/mixins/BindParameterMixin.kt | 2 +- .../cash/sqldelight/dialects/mysql/grammar/MySql.bnf | 2 +- .../mysql/grammar/mixins/BindParameterMixin.kt | 11 +++++++++++ .../postgresql/grammar/mixins/BindParameterMixin.kt | 6 +++++- .../dialect/grammar/mixins/BindParameterMixin.kt | 2 +- .../cash/sqldelight/core/compiler/QueryGenerator.kt | 2 +- .../sqldelight/dialect/DialectIntegrationTests.kt | 9 +++++++++ .../src/test/integration-mysql-async/build.gradle | 4 ++-- .../src/test/integration-mysql-async/settings.gradle | 2 +- .../sqldelight/mysql/integration/{ => async}/Dates.sq | 0 .../sqldelight/mysql/integration/{ => async}/Dog.sq | 0 .../mysql/integration/{ => async}/MySqlTest.kt | 11 +++++++---- 12 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/mixins/BindParameterMixin.kt rename sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/{ => async}/Dates.sq (100%) rename sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/{ => async}/Dog.sq (100%) rename sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/{ => async}/MySqlTest.kt (79%) diff --git a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt index d94f1e65dd8..75148787bf5 100644 --- a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt +++ b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt @@ -4,5 +4,5 @@ import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin import com.intellij.lang.ASTNode abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { - override val replaceWith: String = if (text == "DEFAULT") text else "?" + override fun replaceWith(isAsync: Boolean, index: Int): String = if (text == "DEFAULT") text else "?" } diff --git a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf index 59e5472b200..a9759bf100c 100644 --- a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf +++ b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf @@ -112,7 +112,7 @@ column_constraint ::= [ CONSTRAINT {identifier} ] ( override = true } bind_parameter ::= ( '?' | ':' {identifier} ) { - mixin = "app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin" + mixin = "app.cash.sqldelight.dialects.mysql.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" override = true diff --git a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/mixins/BindParameterMixin.kt b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/mixins/BindParameterMixin.kt new file mode 100644 index 00000000000..4b7dcc667fd --- /dev/null +++ b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/mixins/BindParameterMixin.kt @@ -0,0 +1,11 @@ +package app.cash.sqldelight.dialects.mysql.grammar.mixins + +import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin +import com.intellij.lang.ASTNode + +abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { + override fun replaceWith(isAsync: Boolean, index: Int): String = when (text) { + "DEFAULT" -> text + else -> "?" + } +} diff --git a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt index 4598e8194e9..151dddb0de4 100644 --- a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt +++ b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt @@ -4,5 +4,9 @@ import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin import com.intellij.lang.ASTNode abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { - override val replaceWith: String = if (text == "DEFAULT") text else "?" + override fun replaceWith(isAsync: Boolean, index: Int): String = when { + text == "DEFAULT" -> text + isAsync -> "$$index" + else -> "?" + } } diff --git a/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt b/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt index 7461ee48bf1..ec7aa13969d 100644 --- a/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt +++ b/sqldelight-compiler/dialect/src/main/kotlin/app/cash/sqldelight/dialect/grammar/mixins/BindParameterMixin.kt @@ -11,5 +11,5 @@ abstract class BindParameterMixin(node: ASTNode) : SqlCompositeElementImpl(node) * Some sql dialects support other bind parameter besides `?`, but sqldelight should still replace the * user provided parameter with [replaceWith] for a homogen generated code. */ - open val replaceWith: String = "?" + open fun replaceWith(isAsync: Boolean, index: Int): String = "?" } diff --git a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt index c5fe62ddaf5..08ad4786b9b 100644 --- a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt +++ b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt @@ -206,7 +206,7 @@ abstract class QueryGenerator( // This allows us to use the same algorithm for non Sqlite dialects // :name becomes ? if (bindParameter != null) { - replacements.add(bindArg.range to bindParameter.replaceWith) + replacements.add(bindArg.range to bindParameter.replaceWith(generateAsync, index = nonArrayBindArgsCount)) } } } diff --git a/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt b/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt index 0ea5af6bffa..a66ec9427f0 100644 --- a/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt +++ b/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt @@ -16,6 +16,15 @@ class DialectIntegrationTests { Truth.assertThat(result.output).contains("BUILD SUCCESSFUL") } + @Test fun integrationTestsMySqlAsync() { + val runner = GradleRunner.create() + .withCommonConfiguration(File("src/test/integration-mysql-async")) + .withArguments("clean", "check", "--stacktrace") + + val result = runner.build() + Truth.assertThat(result.output).contains("BUILD SUCCESSFUL") + } + @Test fun integrationTestsMySqlSchemaDefinitions() { val runner = GradleRunner.create() .withCommonConfiguration(File("src/test/integration-mysql-schema")) diff --git a/sqldelight-gradle-plugin/src/test/integration-mysql-async/build.gradle b/sqldelight-gradle-plugin/src/test/integration-mysql-async/build.gradle index f50a9ed2829..8b1f9614cb1 100644 --- a/sqldelight-gradle-plugin/src/test/integration-mysql-async/build.gradle +++ b/sqldelight-gradle-plugin/src/test/integration-mysql-async/build.gradle @@ -7,7 +7,7 @@ apply plugin: 'app.cash.sqldelight' sqldelight { MyDatabase { - packageName = "app.cash.sqldelight.mysql.integration" + packageName = "app.cash.sqldelight.mysql.integration.async" dialect("app.cash.sqldelight:mysql-dialect:${app.cash.sqldelight.VersionKt.VERSION}") generateAsync = true } @@ -26,7 +26,7 @@ dependencies { implementation "org.testcontainers:r2dbc:1.16.2" implementation "dev.miku:r2dbc-mysql:0.8.2.RELEASE" implementation "app.cash.sqldelight:r2dbc-driver:${app.cash.sqldelight.VersionKt.VERSION}" - implementation "app.cash.sqldelight:coroutines-extensions:${app.cash.sqldelight.VersionKt.VERSION}" + implementation "app.cash.sqldelight:async-extensions:${app.cash.sqldelight.VersionKt.VERSION}" implementation libs.truth implementation libs.kotlin.coroutines.core implementation libs.kotlin.coroutines.test diff --git a/sqldelight-gradle-plugin/src/test/integration-mysql-async/settings.gradle b/sqldelight-gradle-plugin/src/test/integration-mysql-async/settings.gradle index 5b846acd802..4995799c151 100644 --- a/sqldelight-gradle-plugin/src/test/integration-mysql-async/settings.gradle +++ b/sqldelight-gradle-plugin/src/test/integration-mysql-async/settings.gradle @@ -1,3 +1,3 @@ apply from: "../settings.gradle" -rootProject.name = 'sqldelight-mysql-integration' +rootProject.name = 'sqldelight-mysql-integration-async' diff --git a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dates.sq b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dates.sq similarity index 100% rename from sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dates.sq rename to sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dates.sq diff --git a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dog.sq b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dog.sq similarity index 100% rename from sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/Dog.sq rename to sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dog.sq diff --git a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/MySqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/async/MySqlTest.kt similarity index 79% rename from sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/MySqlTest.kt rename to sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/async/MySqlTest.kt index 259142b9261..56fa6b1ff29 100644 --- a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/MySqlTest.kt +++ b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/async/MySqlTest.kt @@ -1,5 +1,8 @@ -package app.cash.sqldelight.mysql.integration +package app.cash.sqldelight.mysql.integration.async +import app.cash.sqldelight.async.coroutines.awaitAsList +import app.cash.sqldelight.async.coroutines.awaitAsOne +import app.cash.sqldelight.async.coroutines.awaitCreate import app.cash.sqldelight.driver.r2dbc.R2dbcDriver import com.google.common.truth.Truth.assertThat import io.r2dbc.spi.ConnectionFactories @@ -13,13 +16,13 @@ class MySqlTest { val connection = factory.create().awaitSingle() val driver = R2dbcDriver(connection) - val db = MyDatabase(driver).also { MyDatabase.Schema.create(driver) } + val db = MyDatabase(driver).also { MyDatabase.Schema.awaitCreate(driver) } block(db) } @Test fun simpleSelect() = runTest { database -> database.dogQueries.insertDog("Tilda", "Pomeranian", true) - assertThat(database.dogQueries.selectDogs().executeAsOne()) + assertThat(database.dogQueries.selectDogs().awaitAsOne()) .isEqualTo( Dog( name = "Tilda", @@ -40,7 +43,7 @@ class MySqlTest { dogQueries.selectDogsByBreedAndNames( breed = "Pomeranian", name = listOf("Tilda", "Buddy"), - ).executeAsList(), + ).awaitAsList(), ) .containsExactly( Dog( From cd163e9c281ff84f17d926eabd1707fc1d5ad972 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Thu, 22 Sep 2022 18:51:11 +0200 Subject: [PATCH 14/19] Add postgresql async test --- .../core/compiler/QueryGenerator.kt | 2 +- .../dialect/DialectIntegrationTests.kt | 9 + .../integration-postgresql-async/build.gradle | 33 ++++ .../settings.gradle | 3 + .../postgresql/integration/async/Arrays.sq | 9 + .../postgresql/integration/async/Dates.sq | 17 ++ .../postgresql/integration/async/Dog.sq | 23 +++ .../postgresql/integration/async/OneEntity.sq | 14 ++ .../postgresql/integration/async/WithLock.sq | 25 +++ .../postgresql/integration/PostgreSqlTest.kt | 162 ++++++++++++++++++ 10 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/build.gradle create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/settings.gradle create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Arrays.sq create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dates.sq create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dog.sq create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/OneEntity.sq create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/WithLock.sq create mode 100644 sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt diff --git a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt index 08ad4786b9b..3702e388755 100644 --- a/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt +++ b/sqldelight-compiler/src/main/kotlin/app/cash/sqldelight/core/compiler/QueryGenerator.kt @@ -297,7 +297,7 @@ abstract class QueryGenerator( """ if (result%L == 0L) throw %T(%S) """.trimIndent(), - if (generateAsync) ".await()" else ".value", + if (generateAsync) "" else ".value", ClassName("app.cash.sqldelight.db", "OptimisticLockException"), "UPDATE on ${query.tablesAffected.single().name} failed because optimistic lock ${optimisticLock.name} did not match", ) diff --git a/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt b/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt index a66ec9427f0..256921c818d 100644 --- a/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt +++ b/sqldelight-gradle-plugin/src/dockerTest/kotlin/app/cash/sqldelight/dialect/DialectIntegrationTests.kt @@ -43,6 +43,15 @@ class DialectIntegrationTests { Truth.assertThat(result.output).contains("BUILD SUCCESSFUL") } + @Test fun integrationTestsPostgreSqlAsync() { + val runner = GradleRunner.create() + .withCommonConfiguration(File("src/test/integration-postgresql-async")) + .withArguments("clean", "check", "--stacktrace") + + val result = runner.build() + Truth.assertThat(result.output).contains("BUILD SUCCESSFUL") + } + @Test fun `dialect accepts version catalog dependency`() { val runner = GradleRunner.create() .withCommonConfiguration(File("src/test/integration-catalog")) diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/build.gradle b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/build.gradle new file mode 100644 index 00000000000..601620d8c43 --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/build.gradle @@ -0,0 +1,33 @@ +buildscript { + apply from: "${projectDir.absolutePath}/../buildscript.gradle" +} + +apply plugin: 'org.jetbrains.kotlin.jvm' +apply plugin: 'app.cash.sqldelight' + +sqldelight { + MyDatabase { + packageName = "app.cash.sqldelight.postgresql.integration.async" + dialect("app.cash.sqldelight:postgresql-dialect:${app.cash.sqldelight.VersionKt.VERSION}") + generateAsync = true + } +} + +repositories { + maven { + url "file://${projectDir.absolutePath}/../../../../build/localMaven" + } + mavenCentral() +} + +dependencies { + implementation libs.postgresJdbc + implementation "org.testcontainers:postgresql:1.16.2" + implementation "org.testcontainers:r2dbc:1.16.2" + implementation "org.postgresql:r2dbc-postgresql:0.9.2.RELEASE" + implementation "app.cash.sqldelight:r2dbc-driver:${app.cash.sqldelight.VersionKt.VERSION}" + implementation "app.cash.sqldelight:async-extensions:${app.cash.sqldelight.VersionKt.VERSION}" + implementation libs.truth + implementation libs.kotlin.coroutines.test + implementation libs.kotlin.coroutines.reactive +} diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/settings.gradle b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/settings.gradle new file mode 100644 index 00000000000..2af80808ac6 --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/settings.gradle @@ -0,0 +1,3 @@ +apply from: "../settings.gradle" + +rootProject.name = 'sqldelight-postgresql-integration-async' diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Arrays.sq b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Arrays.sq new file mode 100644 index 00000000000..d4a440d0ab0 --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Arrays.sq @@ -0,0 +1,9 @@ +CREATE TABLE arrays( + intArray INTEGER[], + textArray TEXT[] +); + +insertAndReturn: +INSERT INTO arrays +VALUES (?, ?) +RETURNING *; \ No newline at end of file diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dates.sq b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dates.sq new file mode 100644 index 00000000000..115883395cf --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dates.sq @@ -0,0 +1,17 @@ +CREATE TABLE dates( + date DATE NOT NULL, + time TIME NOT NULL, + timestamp TIMESTAMP NOT NULL, + timestamp_with_timezone TIMESTAMP WITH TIME ZONE NOT NULL +); + +insertDate: +INSERT INTO dates +VALUES (?, ?, ?, ?) +RETURNING *; + +selectDateTrunc: +SELECT date_trunc('hour', timestamp), date_trunc('hour', timestamp_with_timezone) FROM dates; + +selectNow: +SELECT NOW(); diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dog.sq b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dog.sq new file mode 100644 index 00000000000..138341ababe --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/Dog.sq @@ -0,0 +1,23 @@ +CREATE TABLE dog ( + name VARCHAR(8) NOT NULL, + breed TEXT NOT NULL, + is_good INTEGER NOT NULL DEFAULT 1 +); + +insertDog: +INSERT INTO dog +VALUES (?, ?, DEFAULT); + +selectDogs: +SELECT * +FROM dog; + +selectGoodDogs: +SELECT * +FROM dog +WHERE :someBoolean AND 1 = 1; + +insertAndReturn: +INSERT INTO dog +VALUES (?, ?, DEFAULT) +RETURNING *; diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/OneEntity.sq b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/OneEntity.sq new file mode 100644 index 00000000000..8bad31ffcad --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/OneEntity.sq @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS oneEntity ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL +); + +insert: +INSERT INTO oneEntity(name) +VALUES (?); + +selectAll: +SELECT * FROM oneEntity; + +insertUpdate: +INSERT INTO oneEntity AS one VALUES ? ON CONFLICT(id) DO UPDATE SET id = one.id + EXCLUDED.id, name = EXCLUDED.name; diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/WithLock.sq b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/WithLock.sq new file mode 100644 index 00000000000..bc1648ec9a8 --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/main/sqldelight/app/cash/sqldelight/postgresql/integration/async/WithLock.sq @@ -0,0 +1,25 @@ +CREATE TABLE withLock( + id SERIAL AS VALUE NOT NULL, + version INTEGER AS LOCK NOT NULL DEFAULT 0, + text TEXT NOT NULL +); + +insertText: +INSERT INTO withLock (text) +VALUES (?) +RETURNING *; + +updateText: +UPDATE withLock +SET + text = :text, + version = :version + 1 +WHERE + id = :id AND + version = :version +; + +selectForId: +SELECT * +FROM withLock +WHERE id = :id; \ No newline at end of file diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt new file mode 100644 index 00000000000..fd71b2692a5 --- /dev/null +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt @@ -0,0 +1,162 @@ +package app.cash.sqldelight.postgresql.integration + +import app.cash.sqldelight.async.coroutines.* +import app.cash.sqldelight.db.OptimisticLockException +import app.cash.sqldelight.driver.r2dbc.* +import app.cash.sqldelight.postgresql.integration.async.* +import com.google.common.truth.Truth.assertThat +import io.r2dbc.spi.* +import kotlinx.coroutines.reactive.* +import org.junit.Assert +import org.junit.Test +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.OffsetDateTime +import java.time.ZoneOffset + +class PostgreSqlTest { + private val factory = ConnectionFactories.get("r2dbc:tc:postgresql:///myDb?TC_IMAGE_TAG=9.6.8") + + private fun runTest(block: suspend (MyDatabase) -> Unit) = kotlinx.coroutines.test.runTest { + val connection = factory.create().awaitSingle() + val driver = R2dbcDriver(connection) + val db = MyDatabase(driver) + MyDatabase.Schema.awaitCreate(driver) + block(db) + } + + @Test fun simpleSelect() = runTest { database -> + Assert.assertEquals(0, database.dogQueries.selectDogs().awaitAsList().size) + database.dogQueries.insertDog("Tilda", "Pomeranian") + assertThat(database.dogQueries.selectDogs().awaitAsOne()) + .isEqualTo( + Dog( + name = "Tilda", + breed = "Pomeranian", + is_good = 1, + ), + ) + } + + @Test fun booleanSelect() = runTest { database -> + database.dogQueries.insertDog("Tilda", "Pomeranian") + assertThat(database.dogQueries.selectGoodDogs(true).awaitAsOne()) + .isEqualTo( + Dog( + name = "Tilda", + breed = "Pomeranian", + is_good = 1, + ), + ) + } + + @Test fun returningInsert() = runTest { database -> + assertThat(database.dogQueries.insertAndReturn("Tilda", "Pomeranian").awaitAsOne()) + .isEqualTo( + Dog( + name = "Tilda", + breed = "Pomeranian", + is_good = 1, + ), + ) + } + + @Test fun testDates() = runTest { database -> + assertThat( + database.datesQueries.insertDate( + date = LocalDate.of(2020, 1, 1), + time = LocalTime.of(21, 30, 59, 10000), + timestamp = LocalDateTime.of(2020, 1, 1, 21, 30, 59, 10000), + timestamp_with_timezone = OffsetDateTime.of(1980, 4, 9, 20, 15, 45, 0, ZoneOffset.ofHours(0)), + ).awaitAsOne() + , + ) + .isEqualTo( + Dates( + date = LocalDate.of(2020, 1, 1), + time = LocalTime.of(21, 30, 59, 10000), + timestamp = LocalDateTime.of(2020, 1, 1, 21, 30, 59, 10000), + timestamp_with_timezone = OffsetDateTime.of(1980, 4, 9, 20, 15, 45, 0, ZoneOffset.ofHours(0)), + ), + ) + } + + @Test fun testDateTrunc() = runTest { database -> + database.datesQueries.insertDate( + date = LocalDate.of(2020, 1, 1), + time = LocalTime.of(21, 30, 59, 10000), + timestamp = LocalDateTime.of(2020, 1, 1, 21, 30, 59, 10000), + timestamp_with_timezone = OffsetDateTime.of(1980, 4, 9, 20, 15, 45, 0, ZoneOffset.ofHours(0)), + ).awaitAsOne() + + assertThat( + database.datesQueries.selectDateTrunc().awaitAsOne(), + ) + .isEqualTo( + SelectDateTrunc( + date_trunc = LocalDateTime.of(2020, 1, 1, 21, 0, 0, 0), + date_trunc_ = OffsetDateTime.of(1980, 4, 9, 20, 0, 0, 0, ZoneOffset.ofHours(0)), + ), + ) + } + + @Test fun testSerial() = runTest { database -> + database.run { + oneEntityQueries.transaction { + oneEntityQueries.insert("name1") + oneEntityQueries.insert("name2") + oneEntityQueries.insert("name3") + } + assertThat(oneEntityQueries.selectAll().awaitAsList().map { it.id }).containsExactly(1, 2, 3) + } + } + + @Test fun testArrays() = runTest { database -> + with(database.arraysQueries.insertAndReturn(arrayOf(1, 2), arrayOf("one", "two")).awaitAsOne()) { + assertThat(intArray!!.asList()).containsExactly(1, 2).inOrder() + assertThat(textArray!!.asList()).containsExactly("one", "two").inOrder() + } + } + + @Test fun now() = runTest { database -> + val now = database.datesQueries.selectNow().awaitAsOne() + assertThat(now).isNotNull() + assertThat(now).isGreaterThan(OffsetDateTime.MIN) + } + + @Test fun successfulOptimisticLock() = runTest { database -> + with(database.withLockQueries) { + val row = insertText("sup").awaitAsOne() + + updateText( + id = row.id, + version = row.version, + text = "sup2", + ) + + assertThat(selectForId(row.id).awaitAsOne().text).isEqualTo("sup2") + } + } + + @Test fun unsuccessfulOptimisticLock() = runTest { database -> + with(database.withLockQueries) { + val row = insertText("sup").awaitAsOne() + + updateText( + id = row.id, + version = row.version, + text = "sup2", + ) + + try { + updateText( + id = row.id, + version = row.version, + text = "sup3", + ) + Assert.fail() + } catch (e: OptimisticLockException) { } + } + } +} From a280a97cda2cf6c50346a869d85a26ed952a9792 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Thu, 22 Sep 2022 19:02:15 +0200 Subject: [PATCH 15/19] Fix wildards --- .../integration/{ => async}/PostgreSqlTest.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) rename sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/{ => async}/PostgreSqlTest.kt (92%) diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/async/PostgreSqlTest.kt similarity index 92% rename from sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt rename to sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/async/PostgreSqlTest.kt index fd71b2692a5..960db5deb21 100644 --- a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/PostgreSqlTest.kt +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/async/PostgreSqlTest.kt @@ -1,12 +1,13 @@ -package app.cash.sqldelight.postgresql.integration +package app.cash.sqldelight.postgresql.integration.async -import app.cash.sqldelight.async.coroutines.* +import app.cash.sqldelight.async.coroutines.awaitAsList +import app.cash.sqldelight.async.coroutines.awaitAsOne +import app.cash.sqldelight.async.coroutines.awaitCreate import app.cash.sqldelight.db.OptimisticLockException -import app.cash.sqldelight.driver.r2dbc.* -import app.cash.sqldelight.postgresql.integration.async.* +import app.cash.sqldelight.driver.r2dbc.R2dbcDriver import com.google.common.truth.Truth.assertThat -import io.r2dbc.spi.* -import kotlinx.coroutines.reactive.* +import io.r2dbc.spi.ConnectionFactories +import kotlinx.coroutines.reactive.awaitSingle import org.junit.Assert import org.junit.Test import java.time.LocalDate From 18caef1c9849d6fde69eed2399c570b35368be31 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Thu, 22 Sep 2022 19:08:13 +0200 Subject: [PATCH 16/19] Fix code style --- .../sqldelight/postgresql/integration/async/PostgreSqlTest.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/async/PostgreSqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/async/PostgreSqlTest.kt index 960db5deb21..2d90cebfd70 100644 --- a/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/async/PostgreSqlTest.kt +++ b/sqldelight-gradle-plugin/src/test/integration-postgresql-async/src/test/kotlin/app/cash/sqldelight/postgresql/integration/async/PostgreSqlTest.kt @@ -70,8 +70,7 @@ class PostgreSqlTest { time = LocalTime.of(21, 30, 59, 10000), timestamp = LocalDateTime.of(2020, 1, 1, 21, 30, 59, 10000), timestamp_with_timezone = OffsetDateTime.of(1980, 4, 9, 20, 15, 45, 0, ZoneOffset.ofHours(0)), - ).awaitAsOne() - , + ).awaitAsOne(), ) .isEqualTo( Dates( From 115b2fff634a90b608f86f5b88363823a7c31533 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Fri, 23 Sep 2022 08:11:00 +0200 Subject: [PATCH 17/19] Add default to mysql too --- .../app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf | 2 +- .../cash/sqldelight/dialects/mysql/grammar/MySql.bnf | 2 +- .../dialects/postgresql/grammar/PostgreSql.bnf | 2 +- .../app/cash/sqldelight/mysql/integration/async/Dog.sq | 2 +- .../sqldelight/mysql/integration/async/MySqlTest.kt | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf index b4f77a56edf..12815f8af31 100644 --- a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf +++ b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf @@ -79,7 +79,7 @@ column_constraint ::= [ CONSTRAINT {identifier} ] ( implements = "com.alecstrong.sql.psi.core.psi.SqlColumnConstraint" override = true } -bind_parameter ::= ( DEFAULT | '?' | ':' {identifier} ) { +bind_parameter ::= DEFAULT | ( '?' | ':' {identifier} ) { mixin = "app.cash.sqldelight.dialects.hsql.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" diff --git a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf index a9759bf100c..8ef7a5c7956 100644 --- a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf +++ b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf @@ -111,7 +111,7 @@ column_constraint ::= [ CONSTRAINT {identifier} ] ( implements = "com.alecstrong.sql.psi.core.psi.SqlColumnConstraint" override = true } -bind_parameter ::= ( '?' | ':' {identifier} ) { +bind_parameter ::= DEFAULT ( '?' | ':' {identifier} ) { mixin = "app.cash.sqldelight.dialects.mysql.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" diff --git a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf index dd471ea1754..9d6e5f64c5c 100644 --- a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf +++ b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/PostgreSql.bnf @@ -110,7 +110,7 @@ type_name ::= ( implements = "com.alecstrong.sql.psi.core.psi.SqlTypeName" override = true } -bind_parameter ::= ( DEFAULT | '?' | ':' {identifier} ) { +bind_parameter ::= DEFAULT | ( '?' | ':' {identifier} ) { mixin = "app.cash.sqldelight.dialects.postgresql.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" diff --git a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dog.sq b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dog.sq index 977e509000b..5ffd3d40620 100644 --- a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dog.sq +++ b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/main/sqldelight/app/cash/sqldelight/mysql/integration/async/Dog.sq @@ -6,7 +6,7 @@ CREATE TABLE dog ( insertDog: INSERT INTO dog -VALUES (?, ?, ?); +VALUES (?, ?, DEFAULT); selectDogs: SELECT * diff --git a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/async/MySqlTest.kt b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/async/MySqlTest.kt index 56fa6b1ff29..ec657a6af6b 100644 --- a/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/async/MySqlTest.kt +++ b/sqldelight-gradle-plugin/src/test/integration-mysql-async/src/test/kotlin/app/cash/sqldelight/mysql/integration/async/MySqlTest.kt @@ -21,7 +21,7 @@ class MySqlTest { } @Test fun simpleSelect() = runTest { database -> - database.dogQueries.insertDog("Tilda", "Pomeranian", true) + database.dogQueries.insertDog("Tilda", "Pomeranian") assertThat(database.dogQueries.selectDogs().awaitAsOne()) .isEqualTo( Dog( @@ -35,10 +35,10 @@ class MySqlTest { @Test fun simpleSelectWithIn() = runTest { database -> with(database) { - dogQueries.insertDog("Tilda", "Pomeranian", true) - dogQueries.insertDog("Tucker", "Portuguese Water Dog", true) - dogQueries.insertDog("Cujo", "Pomeranian", false) - dogQueries.insertDog("Buddy", "Pomeranian", true) + dogQueries.insertDog("Tilda", "Pomeranian") + dogQueries.insertDog("Tucker", "Portuguese Water Dog") + dogQueries.insertDog("Cujo", "Pomeranian") + dogQueries.insertDog("Buddy", "Pomeranian") assertThat( dogQueries.selectDogsByBreedAndNames( breed = "Pomeranian", From e3b9ebac101b06a170436ace050a6a2cea4d5021 Mon Sep 17 00:00:00 2001 From: hfhbd Date: Fri, 23 Sep 2022 08:20:31 +0200 Subject: [PATCH 18/19] Downgrade r2dbc spi due binary incompatible with drivers Fix docker test dependencies --- .../kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf | 2 +- .../main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt | 2 +- gradle/libs.versions.toml | 2 +- sqldelight-gradle-plugin/build.gradle | 2 ++ 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf index 8ef7a5c7956..e91c30d3112 100644 --- a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf +++ b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf @@ -111,7 +111,7 @@ column_constraint ::= [ CONSTRAINT {identifier} ] ( implements = "com.alecstrong.sql.psi.core.psi.SqlColumnConstraint" override = true } -bind_parameter ::= DEFAULT ( '?' | ':' {identifier} ) { +bind_parameter ::= DEFAULT | ( '?' | ':' {identifier} ) { mixin = "app.cash.sqldelight.dialects.mysql.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" diff --git a/drivers/r2dbc-driver/src/main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt b/drivers/r2dbc-driver/src/main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt index 346cac32c0c..2e8013f9561 100644 --- a/drivers/r2dbc-driver/src/main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt +++ b/drivers/r2dbc-driver/src/main/kotlin/app/cash/sqldelight/driver/r2dbc/R2dbcDriver.kt @@ -48,7 +48,7 @@ class R2dbcDriver(private val connection: Connection) : SqlDriver { return QueryResult.AsyncValue { val result = prepared.execute().awaitSingle() - return@AsyncValue result.rowsUpdated.awaitFirstOrNull() ?: 0 + return@AsyncValue result.rowsUpdated.awaitFirstOrNull()?.toLong() ?: 0 } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5439bf623bc..087367d2350 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -76,7 +76,7 @@ testhelp = { module = "co.touchlab:testhelp", version.ref = "testhelp" } burst = { module = "com.squareup.burst:burst-junit4", version = "1.2.0" } testParameterInjector = { module = "com.google.testparameterinjector:test-parameter-injector", version = "1.8" } -r2dbc = { module = "io.r2dbc:r2dbc-spi", version = "1.0.0.RELEASE" } +r2dbc = { module = "io.r2dbc:r2dbc-spi", version = "0.9.1.RELEASE" } [plugins] android-library = { id = "com.android.library", version.ref = "agp" } diff --git a/sqldelight-gradle-plugin/build.gradle b/sqldelight-gradle-plugin/build.gradle index 406fa0f96eb..5948c4787c9 100644 --- a/sqldelight-gradle-plugin/build.gradle +++ b/sqldelight-gradle-plugin/build.gradle @@ -124,6 +124,8 @@ tasks.named('dockerTest') { ":sqlite-migrations:publishAllPublicationsToInstallLocallyRepository", ":sqldelight-compiler:publishAllPublicationsToInstallLocallyRepository", ":sqldelight-gradle-plugin:publishAllPublicationsToInstallLocallyRepository", + ":drivers:r2dbc-driver:publishAllPublicationsToInstallLocallyRepository", + ":extensions:async-extensions:publishAllPublicationsToInstallLocallyRepository", ) } From 8dcc05f8cd018e575569ed82d1562290daa0587e Mon Sep 17 00:00:00 2001 From: hfhbd Date: Sat, 1 Oct 2022 13:44:02 +0200 Subject: [PATCH 19/19] Remove useless check for default --- .../cash/sqldelight/dialects/hsql/grammar/hsql.bnf | 2 +- .../hsql/grammar/mixins/BindParameterMixin.kt | 8 -------- .../cash/sqldelight/dialects/mysql/grammar/MySql.bnf | 2 +- .../mysql/grammar/mixins/BindParameterMixin.kt | 11 ----------- .../postgresql/grammar/mixins/BindParameterMixin.kt | 1 - 5 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt delete mode 100644 dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/mixins/BindParameterMixin.kt diff --git a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf index 12815f8af31..65befeda298 100644 --- a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf +++ b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/hsql.bnf @@ -80,7 +80,7 @@ column_constraint ::= [ CONSTRAINT {identifier} ] ( override = true } bind_parameter ::= DEFAULT | ( '?' | ':' {identifier} ) { - mixin = "app.cash.sqldelight.dialects.hsql.grammar.mixins.BindParameterMixin" + mixin = "app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" override = true diff --git a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt b/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt deleted file mode 100644 index 75148787bf5..00000000000 --- a/dialects/hsql/src/main/kotlin/app/cash/sqldelight/dialects/hsql/grammar/mixins/BindParameterMixin.kt +++ /dev/null @@ -1,8 +0,0 @@ -package app.cash.sqldelight.dialects.hsql.grammar.mixins - -import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin -import com.intellij.lang.ASTNode - -abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { - override fun replaceWith(isAsync: Boolean, index: Int): String = if (text == "DEFAULT") text else "?" -} diff --git a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf index e91c30d3112..26505f8f889 100644 --- a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf +++ b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/MySql.bnf @@ -112,7 +112,7 @@ column_constraint ::= [ CONSTRAINT {identifier} ] ( override = true } bind_parameter ::= DEFAULT | ( '?' | ':' {identifier} ) { - mixin = "app.cash.sqldelight.dialects.mysql.grammar.mixins.BindParameterMixin" + mixin = "app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin" extends = "com.alecstrong.sql.psi.core.psi.impl.SqlBindParameterImpl" implements = "com.alecstrong.sql.psi.core.psi.SqlBindParameter" override = true diff --git a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/mixins/BindParameterMixin.kt b/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/mixins/BindParameterMixin.kt deleted file mode 100644 index 4b7dcc667fd..00000000000 --- a/dialects/mysql/src/main/kotlin/app/cash/sqldelight/dialects/mysql/grammar/mixins/BindParameterMixin.kt +++ /dev/null @@ -1,11 +0,0 @@ -package app.cash.sqldelight.dialects.mysql.grammar.mixins - -import app.cash.sqldelight.dialect.grammar.mixins.BindParameterMixin -import com.intellij.lang.ASTNode - -abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { - override fun replaceWith(isAsync: Boolean, index: Int): String = when (text) { - "DEFAULT" -> text - else -> "?" - } -} diff --git a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt index 151dddb0de4..05cd7cb9f77 100644 --- a/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt +++ b/dialects/postgresql/src/main/kotlin/app/cash/sqldelight/dialects/postgresql/grammar/mixins/BindParameterMixin.kt @@ -5,7 +5,6 @@ import com.intellij.lang.ASTNode abstract class BindParameterMixin(node: ASTNode) : BindParameterMixin(node) { override fun replaceWith(isAsync: Boolean, index: Int): String = when { - text == "DEFAULT" -> text isAsync -> "$$index" else -> "?" }