Skip to content

Commit 9401068

Browse files
committed
test: cover UDF value marshalling round-trips (#270)
Add a "UDF value marshalling" testset that round-trips values through a registered identity UDF, exercising both SQLite.sqlvalue (argument unmarshalling) and SQLite.sqlreturn (result marshalling) and asserting exact equality. The Int64-boundary cases (typemax/typemin(Int64), 2^40, 2^31, the Int32 limits and values just past them) close a real coverage gap: nothing in the suite previously passed a UDF an integer outside the Int32 range, so the `Sys.WORD_SIZE == 64` branch in sqlvalue was untested — flipping it to sqlite3_value_int (the 32-bit path) silently truncated large integers with no failing test. The remaining cases cover the Float64, String, blob and NULL branches of sqlvalue. Addresses #270.
1 parent 95e7d94 commit 9401068

1 file changed

Lines changed: 68 additions & 0 deletions

File tree

test/runtests.jl

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,74 @@ end
11541154
end
11551155
end
11561156

1157+
@testset "UDF value marshalling" begin
1158+
# These tests pin down SQLite.sqlvalue, which unmarshals a C
1159+
# sqlite3_value* into a Julia value on the UDF argument hot path. A
1160+
# registered `identity` UDF forces every argument through sqlvalue on
1161+
# the way in and sqlreturn on the way out, so `roundtrip(x)` exercises
1162+
# both marshalling directions for a single value and lets us assert
1163+
# exact round-trip equality. The Int64-boundary cases in particular
1164+
# cover the `Sys.WORD_SIZE == 64` branch of sqlvalue: on a 64-bit build
1165+
# the value must be read with sqlite3_value_int64; sqlite3_value_int
1166+
# would silently truncate anything outside the Int32 range.
1167+
db = SQLite.DB()
1168+
SQLite.register(db, identity; nargs = 1, name = "roundtrip")
1169+
1170+
roundtrip(v) = first(
1171+
DBInterface.execute(db, "SELECT roundtrip(?) AS v", (v,)) |>
1172+
columntable |>
1173+
t -> t.v,
1174+
)
1175+
1176+
@testset "Int64 boundary integers" begin
1177+
for v in (
1178+
typemax(Int64),
1179+
typemin(Int64),
1180+
Int64(2)^40,
1181+
Int64(2)^31, # first value past Int32 max
1182+
Int64(2)^31 - 1, # Int32 max itself
1183+
-(Int64(2)^31) - 1, # first value below Int32 min
1184+
0,
1185+
1,
1186+
-1,
1187+
42,
1188+
-42,
1189+
)
1190+
got = roundtrip(v)
1191+
@test got isa Int64
1192+
@test got == v
1193+
end
1194+
end
1195+
1196+
@testset "Float64 values" begin
1197+
for v in (0.0, 3.5, -2.25, 6.4, floatmax(Float64))
1198+
got = roundtrip(v)
1199+
@test got isa Float64
1200+
@test got == v
1201+
end
1202+
end
1203+
1204+
@testset "String values" begin
1205+
# includes multi-byte UTF-8: sqlite3_value_text returns bytes, so
1206+
# codeunit/character confusion would corrupt non-ASCII round-trips
1207+
for v in ("", "abc", "a longer string with spaces", "příliš žluťoučký kůň 🐎 ∀x")
1208+
got = roundtrip(v)
1209+
@test got isa String
1210+
@test got == v
1211+
end
1212+
end
1213+
1214+
@testset "blob values" begin
1215+
v = UInt8[0x00, 0x01, 0xfe, 0xff]
1216+
got = roundtrip(v)
1217+
@test got == v
1218+
end
1219+
1220+
@testset "NULL value" begin
1221+
@test roundtrip(missing) === missing
1222+
end
1223+
end
1224+
11571225
@testset "serialization" begin
11581226
@testset "serialization edgecases" begin
11591227
db = SQLite.DB()

0 commit comments

Comments
 (0)