Skip to content

Commit f3451cc

Browse files
authored
Add Sqlite blob type (#70)
1 parent e7a9075 commit f3451cc

3 files changed

Lines changed: 88 additions & 3 deletions

File tree

ormin/ormin_sqlite.nim

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type
1111
DbConn* = PSqlite3 ## encapsulates a database connection
1212

1313
varcharType* = string
14+
blobType* = seq[byte]
1415
intType* = int
1516
floatType* = float
1617
boolType* = bool
@@ -36,7 +37,15 @@ template startBindings*(s: PStmt; n: int) =
3637
template bindParam*(db: DbConn; s: PStmt; idx: int; x, t: untyped) =
3738
#when not (x is t):
3839
# {.error: "type mismatch for query argument at position " & $idx.}
39-
when t is int or t is int64 or t is bool:
40+
when t is blobType:
41+
let xs = x
42+
let blobPtr = if xs.len == 0:
43+
cast[pointer](nil)
44+
else:
45+
cast[pointer](unsafeAddr(xs[0]))
46+
if bind_blob(s, idx.cint, blobPtr, xs.len.cint, SQLITE_STATIC) != SQLITE_OK:
47+
dbError(db)
48+
elif t is int or t is int64 or t is bool:
4049
if bind_int64(s, idx.cint, x.int64) != SQLITE_OK: dbError(db)
4150
elif t is string:
4251
if bind_text(s, idx.cint, cstring(x), x.len.cint, SQLITE_STATIC) != SQLITE_OK:
@@ -75,7 +84,7 @@ template bindFromJson*(db: DbConn; s: PStmt; idx: int; x: JsonNode;
7584
t: typedesc[string]) =
7685
doAssert x.kind == JString
7786
let xs = x.str
78-
if bind_blob(s, idx.cint, cstring(xs), xs.len.cint, SQLITE_STATIC) != SQLITE_OK:
87+
if bind_text(s, idx.cint, cstring(xs), xs.len.cint, SQLITE_STATIC) != SQLITE_OK:
7988
dbError(db)
8089

8190
template bindFromJson*(db: DbConn; s: PStmt; idx: int; x: JsonNode;
@@ -127,12 +136,27 @@ proc fillString(dest: var string; src: cstring; srcLen: int) =
127136
else: setLen(dest, srcLen)
128137
copyMem(unsafeAddr(dest[0]), src, srcLen)
129138

139+
proc fillBytes(dest: var seq[byte]; src: pointer; srcLen: int) =
140+
if srcLen == 0: return
141+
when defined(nimNoNilSeqs):
142+
setLen(dest, srcLen)
143+
else:
144+
if dest.isNil: dest = newSeq[byte](srcLen)
145+
else: setLen(dest, srcLen)
146+
copyMem(unsafeAddr(dest[0]), src, srcLen)
147+
130148
template bindResult*(db: DbConn; s: PStmt; idx: int; dest: var string;
131149
t: typedesc; name: string) =
132150
let srcLen = column_bytes(s, idx.cint)
133151
let src = column_text(s, idx.cint)
134152
fillString(dest, src, srcLen)
135153

154+
template bindResult*(db: DbConn; s: PStmt; idx: int; dest: var blobType;
155+
t: typedesc; name: string) =
156+
let srcLen = column_bytes(s, idx.cint)
157+
let src = column_blob(s, idx.cint)
158+
fillBytes(dest, src, srcLen)
159+
136160
template bindResult*(db: DbConn; s: PStmt; idx: int; dest: float64;
137161
t: typedesc; name: string) =
138162
dest = column_double(s, idx.cint)
@@ -184,6 +208,17 @@ template bindToJson*(db: DbConn; s: PStmt; idx: int; obj: JsonNode;
184208
t: typedesc[int|int64]; name: string) =
185209
obj[name] = newJInt(column_int64(s, idx.cint))
186210

211+
template bindToJson*(db: DbConn; s: PStmt; idx: int; obj: JsonNode;
212+
t: typedesc[blobType]; name: string) =
213+
let srcLen = column_bytes(s, idx.cint)
214+
let src = column_blob(s, idx.cint)
215+
var data: blobType
216+
fillBytes(data, src, srcLen)
217+
let arr = newJArray()
218+
for b in data:
219+
arr.add newJInt(int(b))
220+
obj[name] = arr
221+
187222
template bindToJson*(db: DbConn; s: PStmt; idx: int; obj: JsonNode;
188223
t: typedesc[float64]; name: string) =
189224
obj[name] = newJFloat(column_double(s, idx.cint))

tests/model_sqlite.sql

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,9 @@ create table if not exists tb_timestamp(
2222

2323
create table if not exists tb_json(
2424
typjson json not null
25-
);
25+
);
26+
27+
create table if not exists tb_blob(
28+
id integer primary key,
29+
typblob blob not null
30+
);

tests/tsqlite.nim

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ let
2424
"dt2": dt2.format(jsonTimeFormat)}
2525
let insertSql = sql"insert into tb_timestamp(dt1, dt2) values (?, ?)"
2626

27+
proc blobFromBytes(bytes: openArray[int]): seq[byte] =
28+
result = newSeq[byte](bytes.len)
29+
for i, b in bytes:
30+
doAssert b >= 0 and b <= 255
31+
result[i] = byte(b)
32+
33+
let blobFixtures = [
34+
blobFromBytes(@[0, 1, 2, 3, 4, 5, 6, 7]),
35+
blobFromBytes(@[255, 128, 64, 32, 16, 8, 4, 2, 1]),
36+
blobFromBytes(@[ord('O'), ord('R'), ord('M'), ord('I'), ord('N'), 0, 1, 2])
37+
]
38+
2739
suite "timestamp_insert":
2840
setup:
2941
db.dropTable(sqlFile, "tb_timestamp")
@@ -94,3 +106,36 @@ suite "timestamp":
94106
where dt1 == %dtjson["dt1"]
95107
produce json
96108
check res == %*[dtjson]
109+
110+
suite "blob_insert":
111+
setup:
112+
db.dropTable(sqlFile, "tb_blob")
113+
db.createTable(sqlFile, "tb_blob")
114+
115+
test "insert parameters":
116+
for blob in blobFixtures:
117+
query:
118+
insert tb_blob(typblob = ?blob)
119+
check db.getValue(sql"select count(*) from tb_blob") == $blobFixtures.len
120+
121+
suite "blob":
122+
db.dropTable(sqlFile, "tb_blob")
123+
db.createTable(sqlFile, "tb_blob")
124+
125+
for blob in blobFixtures:
126+
query:
127+
insert tb_blob(typblob = ?blob)
128+
doAssert db.getValue(sql"select count(*) from tb_blob") == $blobFixtures.len
129+
130+
test "query":
131+
let res = query:
132+
select tb_blob(id, typblob)
133+
check res.mapIt(it.typblob) == blobFixtures
134+
135+
test "where":
136+
let target = blobFixtures[1]
137+
let res = query:
138+
select tb_blob(id, typblob)
139+
where typblob == ?target
140+
check res.len == 1
141+
check res[0].typblob == target

0 commit comments

Comments
 (0)