Open mikkmart opened 1 month ago
Thanks, good catch. This is where I see the warning:
library(DBI)
con <- dbConnect(RSQLite::SQLite(), ":memory:")
dbExecute(con, "CREATE TABLE foo (bar TEXT)")
#> [1] 0
res <- dbSendStatement(con, "INSERT INTO foo (bar) VALUES (?)")
dbBind(res, list("baz"))
dbFetch(res) # <- Should be dbGetRowsAffected()
#> Warning in result_fetch(res@ptr, n = n): SQL statements must be issued with
#> dbExecute() or dbSendStatement() instead of dbGetQuery() or dbSendQuery().
#> data frame with 0 columns and 0 rows
dbClearResult(res)
dbDisconnect(con)
Created on 2024-10-16 with reprex v2.1.1
Interestingly, a misuse of dbSendQuery()
does not trigger the warning:
library(DBI)
con <- dbConnect(RSQLite::SQLite(), ":memory:")
dbExecute(con, "CREATE TABLE foo (bar TEXT)")
#> [1] 0
res <- dbSendQuery(con, "INSERT INTO foo (bar) VALUES (?)")
dbBind(res, list("baz"))
dbGetRowsAffected(res)
#> [1] 1
dbClearResult(res)
dbDisconnect(con)
Created on 2024-10-16 with reprex v2.1.1
I think the best we can do with reasonable effort is to change the wording of the warning. Would you like to contribute?
Yeah I'm happy to put a little PR together.
There's a bit of complexity here from the 3 failure modes to report in one warning. Is the below too much?
library(DBI)
con <- dbConnect(RSQLite::SQLite(), ":memory:")
dbExecute(con, "CREATE TABLE foo (bar TEXT)")
#> [1] 0
# Should warn about misuse of dbGetQuery().
dbGetQuery(con, "INSERT INTO foo (bar) VALUES ('baz')")
#> Warning in result_fetch(res@ptr, n = n): `dbGetQuery()`, `dbSendQuery()` and
#> `dbFetch()` should only be used with `SELECT` queries. Did you mean
#> `dbExecute()`, `dbSendStatement()` or `dbGetRowsAffected()`?
#> data frame with 0 columns and 0 rows
# Should warn about misuse of dbSendQuery()? Maybe already before the fetch?
res <- dbSendQuery(con, "INSERT INTO foo (bar) VALUES ('baz')")
dbFetch(res)
#> Warning in result_fetch(res@ptr, n = n): `dbGetQuery()`, `dbSendQuery()` and
#> `dbFetch()` should only be used with `SELECT` queries. Did you mean
#> `dbExecute()`, `dbSendStatement()` or `dbGetRowsAffected()`?
#> data frame with 0 columns and 0 rows
dbClearResult(res)
# Should warn about misuse of dbFetch().
res <- dbSendStatement(con, "INSERT INTO foo (bar) VALUES ('baz')")
dbFetch(res)
#> Warning in result_fetch(res@ptr, n = n): `dbGetQuery()`, `dbSendQuery()` and
#> `dbFetch()` should only be used with `SELECT` queries. Did you mean
#> `dbExecute()`, `dbSendStatement()` or `dbGetRowsAffected()`?
#> data frame with 0 columns and 0 rows
dbClearResult(res)
# Should remain to be OK.
res <- dbSendStatement(con, "INSERT INTO foo (bar) VALUES ('baz') RETURNING *")
dbFetch(res)
#> bar
#> 1 baz
dbClearResult(res)
dbDisconnect(con)
I guess ideally we'd have something like this:
dbGetQuery(con, "INSERT INTO foo (bar) VALUES ('baz')")
#> Warning: `dbGetQuery()` should only be used with `SELECT` queries. Did you mean `dbExecute()`?
res <- dbSendQuery(con, "INSERT INTO foo (bar) VALUES ('baz')")
#> Warning: `dbSendQuery()` should only be used with `SELECT` queries. Did you mean `dbSendStatement()`?
dbFetch(res)
res <- dbSendStatement(con, "INSERT INTO foo (bar) VALUES ('baz')")
dbFetch(res)
#> Warning: `dbFetch()` should only be used with `SELECT` queries. Did you mean `dbGetRowsAffected()`?
The problem is that the only way to tell if a statement is a SELECT
statement in the SQLite 3 API seems to be with an authorizer which is set on the connection level. Would it be overkill to have dbSendQuery()
set a temporary authorizer to warn about use with statements other than SELECT
?
And actually when it comes to dbFetch()
it doesn't know whether dbSendQuery()
or dbSendStatement()
was used to create the result set, so it would always just warn with a non-SELECT
y result like it does currently.
Calling
dbFetch()
on a result ofdbSendStatement()
gives a confusing warning, telling you to usedbSendStatement()
:The duckdb driver gives a slightly different warning which got me on track in the end:
Ideally I’d like to hear something like “use
dbGetRowsAffected()
instead”.