Skip to content

Commit

Permalink
Support grouping multiple statements together in .sq files (#1801)
Browse files Browse the repository at this point in the history
  • Loading branch information
Alec Strong committed Jun 20, 2020
1 parent 99f1a8e commit 333ae50
Show file tree
Hide file tree
Showing 18 changed files with 324 additions and 26 deletions.
1 change: 1 addition & 0 deletions docs/android_sqlite/grouping_statements.md
@@ -0,0 +1 @@
{% include 'common/grouping_statements.md' %}
13 changes: 13 additions & 0 deletions docs/common/grouping_statements.md
@@ -0,0 +1,13 @@
You can group multiple SQL statements together to be execute at once:

```sql
upsert {
UPDATE myTable
SET column1 = :column1,
column2 = :column2
WHERE id = :id;

INSERT OR IGNORE INTO myTable (id, column1, column2)
VALUES (:column1, :column2, :column3);
}
```
1 change: 1 addition & 0 deletions docs/jvm_h2/grouping_statements.md
@@ -0,0 +1 @@
{% include 'common/grouping_statements.md' %}
1 change: 1 addition & 0 deletions docs/jvm_mysql/grouping_statements.md
@@ -0,0 +1 @@
{% include 'common/grouping_statements.md' %}
1 change: 1 addition & 0 deletions docs/jvm_postgresql/grouping_statements.md
@@ -0,0 +1 @@
{% include 'common/grouping_statements.md' %}
1 change: 1 addition & 0 deletions docs/jvm_sqlite/grouping_statements.md
@@ -0,0 +1 @@
{% include 'common/grouping_statements.md' %}
1 change: 1 addition & 0 deletions docs/multiplatform_sqlite/grouping_statements.md
@@ -0,0 +1 @@
{% include 'common/grouping_statements.md' %}
1 change: 1 addition & 0 deletions docs/native_sqlite/grouping_statements.md
@@ -0,0 +1 @@
{% include 'common/grouping_statements.md' %}
7 changes: 7 additions & 0 deletions mkdocs.yml
Expand Up @@ -20,6 +20,7 @@ nav:
- 'Arguments': android_sqlite/query_arguments.md
- 'Types': android_sqlite/types.md
- 'Transactions': android_sqlite/transactions.md
- 'Grouping Statements': android_sqlite/grouping_statements.md
- 'Extensions':
- 'RxJava': android_sqlite/rxjava.md
- 'Coroutines': android_sqlite/coroutines.md
Expand All @@ -46,6 +47,7 @@ nav:
- 'Arguments': multiplatform_sqlite/query_arguments.md
- 'Types': multiplatform_sqlite/types.md
- 'Transactions': multiplatform_sqlite/transactions.md
- 'Grouping Statements': multiplatform_sqlite/grouping_statements.md
- 'Extensions':
- 'Coroutines': multiplatform_sqlite/coroutines.md
- 'Migrations': multiplatform_sqlite/migrations.md
Expand All @@ -63,6 +65,7 @@ nav:
- 'Arguments': jvm_mysql/query_arguments.md
- 'Types': jvm_mysql/types.md
- 'Transactions': jvm_mysql/transactions.md
- 'Grouping Statements': jvm_mysql/grouping_statements.md
- 'Migrations': jvm_mysql/migrations.md
- 'IntelliJ Plugin': jvm_mysql/intellij_plugin.md
- 'Gradle': jvm_mysql/gradle.md
Expand All @@ -77,6 +80,7 @@ nav:
- 'Arguments': jvm_postgresql/query_arguments.md
- 'Types': jvm_postgresql/types.md
- 'Transactions': jvm_postgresql/transactions.md
- 'Grouping Statements': jvm_postgresql/grouping_statements.md
- 'Migrations': jvm_postgresql/migrations.md
- 'IntelliJ Plugin': jvm_postgresql/intellij_plugin.md
- 'Gradle': jvm_postgresql/gradle.md
Expand All @@ -91,6 +95,7 @@ nav:
- 'Arguments': jvm_h2/query_arguments.md
- 'Types': jvm_h2/types.md
- 'Transactions': jvm_h2/transactions.md
- 'Grouping Statements': jvm_h2/grouping_statements.md
- 'Migrations': jvm_h2/migrations.md
- 'IntelliJ Plugin': jvm_h2/intellij_plugin.md
- 'Gradle': jvm_h2/gradle.md
Expand All @@ -105,6 +110,7 @@ nav:
- 'Arguments': native_sqlite/query_arguments.md
- 'Types': native_sqlite/types.md
- 'Transactions': native_sqlite/transactions.md
- 'Grouping Statements': native_sqlite/grouping_statements.md
- 'Extensions':
- 'Coroutines': native_sqlite/coroutines.md
- 'Migrations': native_sqlite/migrations.md
Expand All @@ -122,6 +128,7 @@ nav:
- 'Arguments': jvm_sqlite/query_arguments.md
- 'Types': jvm_sqlite/types.md
- 'Transactions': jvm_sqlite/transactions.md
- 'Grouping Statements': jvm_sqlite/grouping_statements.md
- 'Extensions':
- 'RxJava': jvm_sqlite/rxjava.md
- 'Coroutines': jvm_sqlite/coroutines.md
Expand Down
Expand Up @@ -151,8 +151,7 @@ private class TeamQueriesImpl(
|FROM team
|WHERE inner_type ${ if (inner_type == null) "IS" else "=" } ?
""".trimMargin(), 1) {
bindString(1, if (inner_type == null) null else
database.teamAdapter.inner_typeAdapter.encode(inner_type))
bindString(1, inner_type?.let { database.teamAdapter.inner_typeAdapter.encode(it) })
}

override fun toString(): String = "Team.sq:forInnerType"
Expand Down
@@ -1,14 +1,55 @@
package com.squareup.sqldelight.core.compiler

import com.alecstrong.sql.psi.core.psi.SqlDeleteStmtLimited
import com.alecstrong.sql.psi.core.psi.SqlInsertStmt
import com.alecstrong.sql.psi.core.psi.SqlUpdateStmtLimited
import com.intellij.psi.util.PsiTreeUtil
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.joinToCode
import com.squareup.sqldelight.core.compiler.model.NamedExecute
import com.squareup.sqldelight.core.compiler.model.NamedMutator
import com.squareup.sqldelight.core.compiler.model.NamedQuery
import com.squareup.sqldelight.core.lang.psi.StmtIdentifierMixin
import com.squareup.sqldelight.core.psi.SqlDelightStmtClojureStmtList

open class ExecuteQueryGenerator(private val query: NamedExecute) : QueryGenerator(query) {
protected open fun FunSpec.Builder.notifyQueries() = this
internal open fun queriesUpdated(): List<NamedQuery> {
if (query.statement is SqlDelightStmtClojureStmtList) {
return PsiTreeUtil.findChildrenOfAnyType(
query.statement,
SqlUpdateStmtLimited::class.java,
SqlDeleteStmtLimited::class.java,
SqlInsertStmt::class.java
).flatMap {
MutatorQueryGenerator(when (it) {
is SqlUpdateStmtLimited -> NamedMutator.Update(it, query.identifier as StmtIdentifierMixin)
is SqlDeleteStmtLimited -> NamedMutator.Delete(it, query.identifier as StmtIdentifierMixin)
is SqlInsertStmt -> NamedMutator.Insert(it, query.identifier as StmtIdentifierMixin)
else -> throw IllegalArgumentException("Unexpected statement $it")
}).queriesUpdated()
}.distinct()
}
return emptyList()
}

private fun FunSpec.Builder.notifyQueries(): FunSpec.Builder {
val resultSetsUpdated = queriesUpdated()

if (resultSetsUpdated.isEmpty()) return this

// The list of effected queries:
// (queryWrapper.dataQueries.selectForId + queryWrapper.otherQueries.selectForId)
// TODO: Only notify queries that were dirtied (check using dirtied method).
addStatement("notifyQueries(%L, {%L})",
query.id,
resultSetsUpdated.map { it.queryProperty }.joinToCode(separator = " + "))

return this
}

/**
* The public api to execute [query]
Expand Down
@@ -1,8 +1,6 @@
package com.squareup.sqldelight.core.compiler

import com.alecstrong.sql.psi.core.psi.SqlTypes
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.joinToCode
import com.squareup.sqldelight.core.compiler.model.NamedMutator
import com.squareup.sqldelight.core.compiler.model.NamedQuery
import com.squareup.sqldelight.core.lang.SqlDelightQueriesFile
Expand All @@ -12,8 +10,7 @@ import com.squareup.sqldelight.core.lang.util.referencedTables
class MutatorQueryGenerator(
private val query: NamedMutator
) : ExecuteQueryGenerator(query) {

override fun FunSpec.Builder.notifyQueries(): FunSpec.Builder {
override fun queriesUpdated(): List<NamedQuery> {
val resultSetsUpdated = mutableListOf<NamedQuery>()
query.containingFile.iterateSqlFiles { psiFile ->
if (psiFile !is SqlDelightQueriesFile) return@iterateSqlFiles true
Expand Down Expand Up @@ -54,15 +51,6 @@ class MutatorQueryGenerator(
return@iterateSqlFiles true
}

if (resultSetsUpdated.isEmpty()) return this

// The list of effected queries:
// (queryWrapper.dataQueries.selectForId + queryWrapper.otherQueries.selectForId)
// TODO: Only notify queries that were dirtied (check using dirtied method).
addStatement("notifyQueries(%L, {%L})",
query.id,
resultSetsUpdated.map { it.queryProperty }.joinToCode(separator = " + "))

return this
return resultSetsUpdated
}
}
Expand Up @@ -8,11 +8,12 @@ open class NamedExecute(
identifier: StmtIdentifierMixin,
statement: PsiElement
) : BindableQuery(identifier, statement) {
val name = identifier.name!!
val name = identifier.name!!

override val id: Int
// the sqlFile package name -> com.example.
// sqlFile.name -> test.sq
// name -> query name
get() = getUniqueQueryIdentifier(statement.sqFile().let { "${it.packageName}:${it.name}:$name" })
override val id: Int
// the sqlFile package name -> com.example.
// sqlFile.name -> test.sq
// name -> query name
get() = getUniqueQueryIdentifier(
statement.sqFile().let { "${it.packageName}:${it.name}:$name" })
}
Expand Up @@ -55,7 +55,16 @@ class SqlDelightQueriesFile(
}

internal val namedExecutes by lazy {
sqliteStatements()
val sqlStmtList = PsiTreeUtil.getChildOfType(this, SqlDelightStmtList::class.java)!!

val transactions = sqlStmtList.stmtClojureList.map {
NamedExecute(
identifier = it.stmtIdentifierClojure as StmtIdentifierMixin,
statement = it.stmtClojureStmtList!!
)
}

val statements = sqliteStatements()
.filter {
it.identifier.name != null &&
it.statement.deleteStmtLimited == null &&
Expand All @@ -64,6 +73,8 @@ class SqlDelightQueriesFile(
it.statement.compoundSelectStmt == null
}
.map { NamedExecute(it.identifier, it.statement) }

return@lazy transactions + statements
}

internal val triggers by lazy { triggers(this) }
Expand Down
@@ -0,0 +1,17 @@
package com.squareup.sqldelight.core.lang.psi

import com.alecstrong.sql.psi.core.psi.LazyQuery
import com.alecstrong.sql.psi.core.psi.SqlCompositeElementImpl
import com.intellij.lang.ASTNode
import com.intellij.psi.PsiElement
import com.squareup.sqldelight.core.lang.util.sqFile
import com.squareup.sqldelight.core.psi.SqlDelightStmtClojureStmtList

abstract class ClojureStmtListMixin(
node: ASTNode
) : SqlCompositeElementImpl(node),
SqlDelightStmtClojureStmtList {
override fun tablesAvailable(child: PsiElement): Collection<LazyQuery> {
return sqFile().tablesAvailable(child)
}
}
Expand Up @@ -16,11 +16,13 @@ import com.intellij.psi.impl.GeneratedMarkerVisitor
import com.intellij.psi.impl.source.tree.TreeElement
import com.squareup.sqldelight.core.lang.SqlDelightQueriesFile
import com.squareup.sqldelight.core.psi.SqlDelightStmtIdentifier
import com.squareup.sqldelight.core.psi.SqlDelightStmtIdentifierClojure

abstract class StmtIdentifierMixin(
node: ASTNode
) : ASTWrapperPsiElement(node),
SqlDelightStmtIdentifier,
SqlDelightStmtIdentifierClojure,
SqlAnnotatedElement {
override fun getName() = identifier()?.text

Expand Down
Expand Up @@ -31,12 +31,22 @@
}
overrides ::= column_def | stmt_list | insert_stmt_values

stmt_list ::= import_stmt_list ( stmt_identifier {stmt} ';' ) * {
stmt_list ::= import_stmt_list ( ( stmt_identifier {stmt} ';' ) | stmt_clojure ) * {
implements="com.alecstrong.sql.psi.core.psi.SqlStmtList"
extends="com.alecstrong.sql.psi.core.psi.impl.SqlStmtListImpl"
pin(".*")=2
override = true
}
stmt_clojure ::= stmt_identifier_clojure stmt_clojure_stmt_list '}' {
implements="com.alecstrong.sql.psi.core.psi.SqlCompositeElement"
extends="com.alecstrong.sql.psi.core.psi.SqlCompositeElementImpl"
pin = 1
}
stmt_clojure_stmt_list ::= {stmt} ';' ( {stmt} ';' ) * {
implements="com.alecstrong.sql.psi.core.psi.SqlCompositeElement"
extends="com.squareup.sqldelight.core.lang.psi.ClojureStmtListMixin"
pin(".*") = 1
}
column_def ::= {column_name} {type_name} [ 'AS' ('@' annotation) * java_type_name ] ( {column_constraint} ) * {
implements=[
"com.alecstrong.sql.psi.core.psi.SqlColumnDef";
Expand All @@ -57,9 +67,15 @@ import_stmt ::= 'import' java_type ';' {
extends="com.squareup.sqldelight.core.lang.psi.ImportStmtMixin"
}
import_stmt_list ::= import_stmt *
stmt_identifier ::= [ JAVADOC ] ( {identifier} ':' ) ? {
stmt_identifier ::= [ JAVADOC ] [ {identifier} ':' ] {
extends="com.squareup.sqldelight.core.lang.psi.StmtIdentifierMixin"
implements="com.squareup.sqldelight.core.lang.psi.StmtIdentifier"
pin(".*") = 2
}
stmt_identifier_clojure ::= [ JAVADOC ] {identifier} '{' {
extends="com.squareup.sqldelight.core.lang.psi.StmtIdentifierMixin"
implements="com.squareup.sqldelight.core.psi.SqlDelightStmtIdentifier"
pin = 3
}
annotation ::= java_type ( '(' annotation_value ')'
| '(' {identifier} '=' annotation_value ( ',' {identifier} '=' annotation_value )* ')'
Expand Down

0 comments on commit 333ae50

Please sign in to comment.