Skip to content
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
6 changes: 6 additions & 0 deletions src/SQLite.jl
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,12 @@ end
# get julia type for given column of the given statement
function juliatype(handle, col, scanrows::Bool = false)
stored_typeid = C.sqlite3_column_type(handle, col - 1)
# Rewinding after a scan re-runs the statement, which would repeat the writes of
# e.g. `INSERT ... RETURNING`; such a column gets its declared type or `Any`.
if scanrows && stored_typeid == C.SQLITE_NULL && C.sqlite3_stmt_readonly(handle) == 0
decl_typestr = C.sqlite3_column_decltype(handle, col - 1)
return decl_typestr == C_NULL ? Any : juliatype(unsafe_string(decl_typestr), Any)
end
did_row_scan = false
while scanrows && stored_typeid == C.SQLITE_NULL
# Scan forward through the rows until we find a non-NULL value for this column
Expand Down
4 changes: 3 additions & 1 deletion src/tables.jl
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ function getvalue(
if t == C.SQLITE_NULL
return missing
end
sqlitevalue(nonmissingtype(T), handle, col)
S = nonmissingtype(T)
# An `Any` column holds values of several storage classes; read each by its own.
sqlitevalue(S === Any ? juliatype(t) : S, handle, col)
end

function getvalue(
Expand Down
27 changes: 27 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,33 @@ end
end
end

@testset "Type inference does not repeat writes" begin
db = SQLite.DB()
strict(sql) = columntable(
DBInterface.execute(DBInterface.prepare(db, sql), (); strict = true),
)
DBInterface.execute(db, "CREATE TABLE t (id INTEGER PRIMARY KEY, n INTEGER, note TEXT, extra)")
r = strict("INSERT INTO t (n) VALUES (1), (2) RETURNING id, note, extra")
@test r.id == [1, 2]
@test eltype(r.note) == Union{Missing,String}
@test all(ismissing, r.note) && all(ismissing, r.extra)
@test columntable(DBInterface.execute(db, "SELECT id FROM t")).id == [1, 2]
r = strict("UPDATE t SET n = n + 1, extra = 2.5 WHERE id = 2 RETURNING n, note, extra")
@test r.n == [3] && isequal(r.note, [missing]) && r.extra == [2.5]
r = strict("UPDATE t SET n = n + 1, extra = NULL RETURNING extra")
@test length(r.extra) == 2 && all(ismissing, r.extra)
@test columntable(DBInterface.execute(db, "SELECT n FROM t ORDER BY id")).n == [2, 4]
end

@testset "Strict columns of type Any keep each value's storage class" begin
db = SQLite.DB()
DBInterface.execute(db, "CREATE TABLE b (x BLOB)")
DBInterface.execute(db, "INSERT INTO b VALUES (1), ('txt'), (x'0102'), (NULL)")
stmt = DBInterface.prepare(db, "SELECT x FROM b ORDER BY rowid")
tbl = columntable(DBInterface.execute(stmt, (); strict = true))
@test isequal(tbl.x, [1, "txt", UInt8[0x01, 0x02], missing])
end

@testset "PR #343: strict (and only strict) tables should error if types don't match" begin
db = SQLite.DB()

Expand Down
Loading