-
Notifications
You must be signed in to change notification settings - Fork 515
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix codegen for RETURNING clause #3872
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package app.cash.sqldelight.dialect.api | ||
|
||
import com.alecstrong.sql.psi.core.psi.QueryElement | ||
|
||
internal fun List<QueryElement.QueryColumn>.flattenCompounded(): List<QueryElement.QueryColumn> { | ||
return map { column -> | ||
if (column.compounded.none { it.element != column.element || it.nullable != column.nullable }) { | ||
column.copy(compounded = emptyList()) | ||
} else { | ||
column | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package app.cash.sqldelight.dialect.api | ||
|
||
import com.alecstrong.sql.psi.core.psi.QueryElement | ||
import com.alecstrong.sql.psi.core.psi.Queryable | ||
import com.alecstrong.sql.psi.core.psi.SqlAnnotatedElement | ||
import com.alecstrong.sql.psi.core.psi.SqlTableName | ||
import com.intellij.psi.util.PsiTreeUtil | ||
|
||
/** | ||
* Query deriving from the `RETURNING` clause of an expression. | ||
* | ||
* Typical use cases include `INSERT`, `UPDATE`, and `DELETE`. This class is similar to [SelectQueryable] but differs in | ||
* the fact that only 1 table can be part of the query, and that table is guaranteed to be "real". | ||
* | ||
* @param statement Parent statement. Typically, this is the `INSERT`, `UPDATE`, or `DELETE` statement. | ||
* @param select The `RETURNING` clause of the statement. Represented as a query since it returns values to the caller. | ||
* @param tableName Name of the table the [statement] is operating on. | ||
*/ | ||
class ReturningQueryable( | ||
override var statement: SqlAnnotatedElement, | ||
override val select: QueryElement, | ||
private val tableName: SqlTableName?, | ||
) : QueryWithResults { | ||
|
||
override val pureTable by lazy { | ||
val pureColumns = select.queryExposed().singleOrNull()?.columns?.flattenCompounded() | ||
val resolvedTable = tableName?.reference?.resolve() | ||
val table = PsiTreeUtil.getParentOfType(resolvedTable, Queryable::class.java)?.tableExposed() | ||
?: return@lazy null | ||
val requestedColumnsAreIdenticalToTable = table.query.columns.flattenCompounded() == pureColumns | ||
if (requestedColumnsAreIdenticalToTable) { | ||
tableName | ||
} else { | ||
null | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,6 @@ | ||
package app.cash.sqldelight.dialect.api | ||
|
||
import com.alecstrong.sql.psi.core.psi.NamedElement | ||
import com.alecstrong.sql.psi.core.psi.QueryElement.QueryColumn | ||
import com.alecstrong.sql.psi.core.psi.Queryable | ||
import com.alecstrong.sql.psi.core.psi.SqlAnnotatedElement | ||
import com.alecstrong.sql.psi.core.psi.SqlCompoundSelectStmt | ||
|
@@ -21,16 +20,6 @@ class SelectQueryable( | |
* which points to that table (Pure meaning it has exactly the same columns in the same order). | ||
*/ | ||
override val pureTable: NamedElement? by lazy { | ||
fun List<QueryColumn>.flattenCompounded(): List<QueryColumn> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Names are okay. |
||
return map { column -> | ||
if (column.compounded.none { it.element != column.element || it.nullable != column.nullable }) { | ||
column.copy(compounded = emptyList()) | ||
} else { | ||
column | ||
} | ||
} | ||
} | ||
|
||
val pureColumns = select.queryExposed().singleOrNull()?.columns?.flattenCompounded() | ||
|
||
// First check to see if its just the table we're observing directly. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,8 +81,8 @@ class PgInsertReturningTest { | |
|
||
assertThat(generator.defaultResultTypeFunction().toString()).isEqualTo( | ||
""" | ||
|public fun insertReturn(data_: com.example.Data_): app.cash.sqldelight.ExecutableQuery<com.example.Data_> = insertReturn(data_) { data__, id -> | ||
| com.example.Data_( | ||
|public fun insertReturn(data_: com.example.Data_): app.cash.sqldelight.ExecutableQuery<com.example.InsertReturn> = insertReturn(data_) { data__, id -> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test verified that the code got generated, but the generated code didn't compile. I'd recommend actually deleting the test, given the integration tests have, in my opinion, better coverage, but either way, the test passes now. |
||
| com.example.InsertReturn( | ||
| data__, | ||
| id | ||
| ) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,50 @@ SELECT * | |
FROM dog | ||
WHERE :someBoolean AND 1 = 1; | ||
|
||
insertAndReturn: | ||
insertAndReturn1: | ||
INSERT INTO dog | ||
VALUES (?, ?, DEFAULT) | ||
RETURNING name; | ||
|
||
insertAndReturnMany: | ||
INSERT INTO dog | ||
VALUES (?, ?, DEFAULT) | ||
RETURNING name, breed; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was the problem scenario. |
||
|
||
insertAndReturnAll: | ||
INSERT INTO dog | ||
VALUES (?, ?, DEFAULT) | ||
RETURNING *; | ||
|
||
updateAndReturn1: | ||
UPDATE dog | ||
SET is_good = ? | ||
WHERE name = ? | ||
RETURNING name; | ||
|
||
updateAndReturnMany: | ||
UPDATE dog | ||
SET is_good = ? | ||
WHERE name = ? | ||
RETURNING name, breed; | ||
|
||
updateAndReturnAll: | ||
UPDATE dog | ||
SET is_good = ? | ||
WHERE name = ? | ||
RETURNING *; | ||
|
||
deleteAndReturn1: | ||
DELETE FROM dog | ||
WHERE name = ? | ||
RETURNING name; | ||
|
||
deleteAndReturnMany: | ||
DELETE FROM dog | ||
WHERE name = ? | ||
RETURNING name, breed; | ||
|
||
deleteAndReturnAll: | ||
DELETE FROM dog | ||
WHERE name = ? | ||
RETURNING *; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used by SQLite and Postgres dialects at the moment, but reasonable to think additional dialects would want this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly, thanks.