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

Generated query files return row counts for simple mutators #4578

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

MariusVolkhart
Copy link
Contributor

Simple mutators like INSERT, UPDATE, and DELETE, without RETURNING clauses, typically return the number of rows changed. In fact, the SqlDriver#execute() function already does this. However, the value of execute() is not exposed to the generated query files.

This commit makes it so that simple INSERT, UPDATE, and DELETE statements return the QueryResult that the SqlDriver already returns.

Review Guide

In short, this commit turns

fun updatePlayerName(id: Long, name: String)

into

fun updatePlayerName(id: Long, name: String): QueryResult<Long>

The main logic changes are in ExecuteQueryGenerator and QueryGenerator.

@MariusVolkhart MariusVolkhart force-pushed the mv/mutatorsReturn branch 3 times, most recently from b8054fc to 898868a Compare September 1, 2023 02:29
@morki
Copy link
Contributor

morki commented Sep 2, 2023

This can also somewhat fix #3238 and make the behaviour consistent

@morki
Copy link
Contributor

morki commented Sep 2, 2023

But for backward compatibility, it should be behind some flag which will default to false.

@MariusVolkhart
Copy link
Contributor Author

@morki What backwards compatibility are you concerned about? This doesn't change the query behavior, only the API of generated code (but only the return value, so should be source-compatible).

@morki
Copy link
Contributor

morki commented Oct 24, 2023

@MariusVolkhart as I understand this, it can compile fine, but it changes the behaviour. From your example:

updatePlayerName(4, "Hero")

This now normally executes the query. But with your change, the same compiles fine, but does not execute the query. This code will execute the query:

updatePlayerName(4, "Hero").executeAsOne()

Do I understand it right?

@MariusVolkhart
Copy link
Contributor Author

@morki Oh, gotcha. No, that's not how this works.

In the example,

updatePlayerName(4, "Hero").executeAsOne()

updatePlayerName would have to return an ExecutableQuery. The ExecutableQuery allows the query execution to be deferred and this deference is what would cause a behavior change.

However, in the changes in this PR, updatePlayerName returns a QueryResult. The query is still executed immediately, as it was before. The only change is that the result of the query, which was already reported by the driver, is now exposed. That is to say, this PR does not change query execution behavior, only exposes and additional result that is already immediately available. See https://github.com/cashapp/sqldelight/pull/4578/files#diff-3d7267ef64e0f3f32d17e8f5379be391dcf82cecd259a4b42cff03c8ed105a3e for a good example of how the generated code changes.

@morki
Copy link
Contributor

morki commented Nov 7, 2023

@MariusVolkhart oh, thank you for the explanation and sorry for bad understanding. I was probably tired when quickly reviewing.

@AlecKazakova
Copy link
Collaborator

I think my main concern is how this can leak implementation details of individual drivers. I don't know that all the supported drivers actually return the same thing for each of the mutator queries.

I don't know the right way to solve it. SQLDelight would need some knowledge of how the driver works and choose to return the result if its the thing we want to expose, or run a separate query to get the last_change_id or changes() (in SQLite, IDK what it is for other dialects) so that its guaranteed to behave the same with different drivers

Or we just let them go through as is and its up to the consumer to know what the driver returns, but I don't think thats right

Simple mutators like INSERT, UPDATE, and DELETE, without RETURNING clauses, typically return the number of rows changed. In fact, the SqlDriver#execute() function already does this. However, the value of execute() is not exposed to the generated query files.

This commit makes it so that simple INSERT, UPDATE, and DELETE statements return the QueryResult<Long> that the SqlDriver already returns.
@MariusVolkhart
Copy link
Contributor Author

I lean towards the "we just let them go through as is and it's up to the consumer to know what the driver returns" approach.

Firstly, not exposing the return value limits the capabilities of SQLDelight relative to the underlying driver. SqlDriver already has an API that returns the mutated count, and it's a behavior common in many systems (DBMS, JDBC, etc). It feels like quite a severe, yet artificial, limitation compared to Hibernate, JDBC, etc to not expose the ability to query the mutated count.

Secondly, exposing the mutated count means that people can develop custom drivers for implementations (SQLJS, Sqlite) that do query last_change or changes(), if they want that functionality. This assumes that we don't want such functionality built-in, which is reasonable, since a second query that might not be consumed is undesirable.

Thirdly, developers using a driver that doesn't support this will surely either know, or quickly find out when they try to use it, that their driver doesn't support this functionality.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants