Skip to content
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

Support multi-column-expr when doing a SELECT #4453

Merged
merged 2 commits into from
Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ class IntegrationTest {
| WHERE player.rowid = last_insert_rowid();
|}
|
|greaterThanNumberAndName:
|SELECT *
|FROM player
|WHERE (number, name) > (?, ?);
|
""".trimMargin(),
temporaryFolder,
"Player.sq",
Expand Down Expand Up @@ -331,4 +336,45 @@ class IntegrationTest {
assertThat(brady.name).isEqualTo(Player.Name("Brady Tkachuk"))
}
}

@Test fun `multi column expression select`() {
queryWrapper.playerQueries.insertPlayer(Player.Name("Brady Hockey"), 15, Team.Name("Ottawa Hockey Team"), LEFT)

assertThat(
queryWrapper.playerQueries.greaterThanNumberAndName(10, Player.Name("A")).executeAsList(),
).containsExactly(
Player(Player.Name("Brady Hockey"), 15, Team.Name("Ottawa Hockey Team"), LEFT),
Player(Player.Name("Ryan Getzlaf"), 15, Team.Name("Anaheim Ducks"), RIGHT),
Player(Player.Name("Erik Karlsson"), 65, Team.Name("Ottawa Senators"), RIGHT),
)

assertThat(
queryWrapper.playerQueries.greaterThanNumberAndName(10, Player.Name("Z")).executeAsList(),
).containsExactly(
Player(Player.Name("Brady Hockey"), 15, Team.Name("Ottawa Hockey Team"), LEFT),
Player(Player.Name("Ryan Getzlaf"), 15, Team.Name("Anaheim Ducks"), RIGHT),
Player(Player.Name("Erik Karlsson"), 65, Team.Name("Ottawa Senators"), RIGHT),
)

assertThat(
queryWrapper.playerQueries.greaterThanNumberAndName(15, Player.Name("A")).executeAsList(),
).containsExactly(
Player(Player.Name("Brady Hockey"), 15, Team.Name("Ottawa Hockey Team"), LEFT),
Player(Player.Name("Ryan Getzlaf"), 15, Team.Name("Anaheim Ducks"), RIGHT),
Player(Player.Name("Erik Karlsson"), 65, Team.Name("Ottawa Senators"), RIGHT),
)

assertThat(
queryWrapper.playerQueries.greaterThanNumberAndName(15, Player.Name("C")).executeAsList(),
).containsExactly(
Player(Player.Name("Ryan Getzlaf"), 15, Team.Name("Anaheim Ducks"), RIGHT),
Player(Player.Name("Erik Karlsson"), 65, Team.Name("Ottawa Senators"), RIGHT),
)

assertThat(
queryWrapper.playerQueries.greaterThanNumberAndName(15, Player.Name("Z")).executeAsList(),
).containsExactly(
Player(Player.Name("Erik Karlsson"), 65, Team.Name("Ottawa Senators"), RIGHT),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,34 @@ public class PlayerQueries(
)
}

public fun <T : Any> greaterThanNumberAndName(
number: Long,
name: Player.Name,
mapper: (
name: Player.Name,
number: Long,
team: Team.Name?,
shoots: Shoots,
) -> T,
): Query<T> = GreaterThanNumberAndNameQuery(number, name) { cursor ->
mapper(
Player.Name(cursor.getString(0)!!),
cursor.getLong(1)!!,
cursor.getString(2)?.let { Team.Name(it) },
playerAdapter.shootsAdapter.decode(cursor.getString(3)!!)
)
}

public fun greaterThanNumberAndName(number: Long, name: Player.Name): Query<Player> =
greaterThanNumberAndName(number, name) { name_, number_, team, shoots ->
Player(
name_,
number_,
team,
shoots
)
}

public fun insertPlayer(
name: Player.Name,
number: Long,
Expand Down Expand Up @@ -280,4 +308,30 @@ public class PlayerQueries(

override fun toString(): String = "Player.sq:playersForNumbers"
}

private inner class GreaterThanNumberAndNameQuery<out T : Any>(
public val number: Long,
public val name: Player.Name,
mapper: (SqlCursor) -> T,
) : Query<T>(mapper) {
override fun addListener(listener: Query.Listener) {
driver.addListener("player", listener = listener)
}

override fun removeListener(listener: Query.Listener) {
driver.removeListener("player", listener = listener)
}

override fun <R> execute(mapper: (SqlCursor) -> QueryResult<R>): QueryResult<R> =
driver.executeQuery(-1_258_650_806, """
|SELECT *
|FROM player
|WHERE (number, name) > (?, ?)
""".trimMargin(), mapper, 2) {
bindLong(0, number)
bindString(1, name.name)
}

override fun toString(): String = "Player.sq:greaterThanNumberAndName"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import com.alecstrong.sql.psi.core.psi.SqlInsertStmt
import com.alecstrong.sql.psi.core.psi.SqlInsertStmtValues
import com.alecstrong.sql.psi.core.psi.SqlIsExpr
import com.alecstrong.sql.psi.core.psi.SqlLimitingTerm
import com.alecstrong.sql.psi.core.psi.SqlMultiColumnExpr
import com.alecstrong.sql.psi.core.psi.SqlMultiColumnExpression
import com.alecstrong.sql.psi.core.psi.SqlNullExpr
import com.alecstrong.sql.psi.core.psi.SqlParenExpr
import com.alecstrong.sql.psi.core.psi.SqlResultColumn
Expand Down Expand Up @@ -68,6 +70,14 @@ internal fun SqlExpr.inferredType(): IntermediateType {
}
}

is SqlMultiColumnExpression -> {
val idx = parentRule.exprList.indexOf(this)
val parentParent = parentRule.parent as SqlMultiColumnExpr

// The first multiColumnExpression is the column list
parentParent.multiColumnExpressionList[0].exprList[idx].type()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I assume this will not return null?

From https://github.com/AlecStrong/sql-psi/blob/master/core/src/main/kotlin/com/alecstrong/sql/psi/core/sql.bnf#L237

these two expressions are not optional.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

}

is SqlValuesExpression -> parentRule.argumentType(this)
is SqlSetterExpression -> typeResolver.argumentType(parentRule, this)
is SqlLimitingTerm -> IntermediateType(INTEGER)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1868,4 +1868,55 @@ class SelectQueryTypeTest {
""".trimMargin(),
)
}

@Test
fun `multi column expression select`() {
val file = FixtureCompiler.parseSql(
"""
|CREATE TABLE multi(
| id INTEGER NOT NULL PRIMARY KEY,
| name TEXT NOT NULL
|);
|
|multiColumnExpressionSelect:
|SELECT *
|FROM multi
|WHERE (id, name) > (?, ?);
""".trimMargin(),
tempFolder,
)

val select = file.namedQueries.first()
val generator = SelectQueryGenerator(select)

assertThat(generator.querySubtype().toString()).isEqualTo(
"""
|private inner class MultiColumnExpressionSelectQuery<out T : kotlin.Any>(
| public val id: kotlin.Long,
| public val name: kotlin.String,
| mapper: (app.cash.sqldelight.db.SqlCursor) -> T,
|) : app.cash.sqldelight.Query<T>(mapper) {
| override fun addListener(listener: app.cash.sqldelight.Query.Listener) {
| driver.addListener("multi", listener = listener)
| }
|
| override fun removeListener(listener: app.cash.sqldelight.Query.Listener) {
| driver.removeListener("multi", listener = listener)
| }
|
| override fun <R> execute(mapper: (app.cash.sqldelight.db.SqlCursor) -> app.cash.sqldelight.db.QueryResult<R>): app.cash.sqldelight.db.QueryResult<R> = driver.executeQuery(841_750_630, ""${'"'}
| |SELECT *
| |FROM multi
| |WHERE (id, name) > (?, ?)
| ""${'"'}.trimMargin(), mapper, 2) {
| bindLong(0, id)
| bindString(1, name)
Comment on lines +1912 to +1913
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The types look right here

| }
|
| override fun toString(): kotlin.String = "Test.sq:multiColumnExpressionSelect"
|}
|
""".trimMargin(),
)
}
}