sqlite3: support option type

.. and any integer when writing but don't support int32 on reads -
internally sqlite will truncate on overflow which isn't nice.
This commit is contained in:
Jacek Sieka 2021-05-26 11:34:46 +02:00 committed by zah
parent 2a292cfb62
commit 68e6aadc29
2 changed files with 65 additions and 13 deletions

View File

@ -93,10 +93,13 @@ proc bindParam(s: RawStmtPtr, n: int, val: auto): cint =
sqlite3_bind_blob(s, n.cint, nil, 0.cint, nil) sqlite3_bind_blob(s, n.cint, nil, 0.cint, nil)
else: else:
{.fatal: "Please add support for the '" & $typeof(val) & "' type".} {.fatal: "Please add support for the '" & $typeof(val) & "' type".}
elif val is int32: elif val is SomeInteger:
sqlite3_bind_int(s, n.cint, val) sqlite3_bind_int64(s, n.cint, val.clong)
elif val is int64: elif val is Option:
sqlite3_bind_int64(s, n.cint, val) if val.isNone():
sqlite3_bind_null(s, n.cint)
else:
bindParam(s, n, val.get())
else: else:
{.fatal: "Please add support for the '" & $typeof(val) & "' type".} {.fatal: "Please add support for the '" & $typeof(val) & "' type".}
@ -125,13 +128,15 @@ proc exec*[P](s: SqliteStmt[P, void], params: P): KvResult[void] =
res res
template readResult(s: RawStmtPtr, column: cint, T: type): auto = template readSimpleResult(s: RawStmtPtr, column: cint, T: type): auto =
when T is int32: when T is int64:
sqlite3_column_int(s, column)
elif T is int64:
sqlite3_column_int64(s, column) sqlite3_column_int64(s, column)
elif T is int: elif T is SomeInteger:
{.fatal: "Please use specify either int32 or int64 precisely".} # sqlite integers are "up to" 8 bytes in size, so rather than silently
# truncate them, we support only 64-bit integers when reading and let the
# calling code deal with it - careful though, anything that is not an
# integer (ie TEXT) is returned as 0
{.fatal: "Use int64 for reading integers".}
elif T is openArray[byte]: elif T is openArray[byte]:
let let
p = cast[ptr UncheckedArray[byte]](sqlite3_column_blob(s, column)) p = cast[ptr UncheckedArray[byte]](sqlite3_column_blob(s, column))
@ -161,6 +166,15 @@ template readResult(s: RawStmtPtr, column: cint, T: type): auto =
else: else:
{.fatal: "Please add support for the '" & $(T) & "' type".} {.fatal: "Please add support for the '" & $(T) & "' type".}
template readResult(s: RawStmtPtr, column: cint, T: type): auto =
when T is Option:
if sqlite3_column_type(s, column) == SQLITE_NULL:
none(typeof(default(T).get()))
else:
some(readSimpleResult(s, column, typeof(default(T).get())))
else:
readSimpleResult(s, column, T)
template readResult(s: RawStmtPtr, T: type): auto = template readResult(s: RawStmtPtr, T: type): auto =
when T is tuple: when T is tuple:
var res: T var res: T

View File

@ -1,7 +1,7 @@
{.used.} {.used.}
import import
std/os, std/[os, options],
testutils/unittests, testutils/unittests,
../../eth/db/[kvstore, kvstore_sqlite3], ../../eth/db/[kvstore, kvstore_sqlite3],
./test_kvstore ./test_kvstore
@ -45,7 +45,6 @@ procSuite "SqStoreRef":
NoParams, int64).get NoParams, int64).get
var totalRecords = 0 var totalRecords = 0
echo "About to call total records"
let countRes = countStmt.exec do (res: int64): let countRes = countStmt.exec do (res: int64):
totalRecords = int res totalRecords = int res
@ -138,7 +137,6 @@ procSuite "SqStoreRef":
NoParams, int64).get NoParams, int64).get
var totalRecords = 0 var totalRecords = 0
echo "About to call total attestations"
let countRes = countStmt.exec do (res: int64): let countRes = countStmt.exec do (res: int64):
totalRecords = int res totalRecords = int res
@ -178,3 +176,43 @@ procSuite "SqStoreRef":
source == 2 source == 2
target == 4 target == 4
digest == hash digest == hash
test "null values":
#
let db = SqStoreRef.init("", "test", inMemory = true)[]
defer: db.close()
let createTableRes = db.exec """
CREATE TABLE IF NOT EXISTS testnull(
a INTEGER PRIMARY KEY,
b INTEGER NULL,
c INTEGER NULL
);
"""
check createTableRes.isOk
type
ABC = (int64, Option[int64], Option[int64])
let insertStmt = db.prepareStmt("""
INSERT INTO testnull(a, b, c) VALUES (?,?,?);
""", ABC, void).get()
const val = (42'i64, none(int64), some(44'i64))
check:
insertStmt.exec(val).isOk()
let selectStmt = db.prepareStmt("""
SELECT a, b, c FROM testnull
""", NoParams, ABC).get()
block:
var abc: ABC
let selectRes = selectStmt.exec do (res: ABC):
abc = res
if selectRes.isErr:
echo selectRes.error
check:
selectRes.isOk and selectRes.get == true
abc == val