From e532c50e998f29f9b680ad62bc645b9f666c0a6a Mon Sep 17 00:00:00 2001 From: mahlon Date: Sat, 19 Apr 2025 17:45:53 +0000 Subject: [PATCH] Add tests and docs for toBlob. Bump version. FossilOrigin-Name: d7358c9d8961f2b70f83ca4e19d4c43408e59cc169f072a238745060edb09de9 --- History.md | 8 +++++ USAGE.md | 19 ++++++++++++ kuzu.nimble | 2 +- src/kuzu/constants.nim | 2 +- src/kuzu/value.nim | 34 ++++++++++----------- tests/values/t_can_return_a_blob_object.nim | 32 +++++++++++++++++++ 6 files changed, 78 insertions(+), 19 deletions(-) create mode 100644 tests/values/t_can_return_a_blob_object.nim diff --git a/History.md b/History.md index 2437125..38c49f4 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,13 @@ # Release History for nim-kuzu +--- +## v0.3.0 [2025-04-19] Mahlon E. Smith + +Enhancement: + + - Add `toBlob` for quickly fetching a sequence of opaque bytes. + + --- ## v0.2.0 [2025-04-01] Mahlon E. Smith diff --git a/USAGE.md b/USAGE.md index 2c94e94..8985ab0 100644 --- a/USAGE.md +++ b/USAGE.md @@ -501,3 +501,22 @@ for row in res: # Bob has known Alice since 2009. # Alice has known Bob since 2010. ``` + + +### Blobs + +Kuzu can store small chunks of opaque binary data. For these BLOB columns, +using `toBlob` will return the raw sequence of bytes. + +```nim +var q = conn.query """ +CREATE NODE TABLE Doot ( id SERIAL, data BLOB, PRIMARY KEY(id) ) +""" + +var stmt = conn.prepare( "CREATE (d:Doot {data: encode($str)})" ) +q = stmt.execute( (str: "Hello!") ) +q = conn.query( "MATCH (d:Doot) RETURN d.data" ) + +var blob = q.getNext[0].toBlob #=> @[72, 101, 108, 108, 111, 33] +``` + diff --git a/kuzu.nimble b/kuzu.nimble index 82253a3..fca010f 100644 --- a/kuzu.nimble +++ b/kuzu.nimble @@ -1,6 +1,6 @@ # vim: set et sta sw=4 ts=4 : -version = "0.2.0" +version = "0.3.0" author = "Mahlon E. Smith" description = "Kuzu is an embedded graph database built for query speed and scalability." license = "BSD-3-Clause" diff --git a/src/kuzu/constants.nim b/src/kuzu/constants.nim index b4422b8..c59f37e 100644 --- a/src/kuzu/constants.nim +++ b/src/kuzu/constants.nim @@ -1,6 +1,6 @@ # vim: set et sta sw=4 ts=4 : -const KUZU_VERSION* = "0.2.0" +const KUZU_VERSION* = "0.3.0" const KUZU_EXPECTED_LIBVERSION* = "0.9.0" const BLOB_MAXSIZE = 4096 diff --git a/src/kuzu/value.nim b/src/kuzu/value.nim index 0f493e0..d5fc16b 100644 --- a/src/kuzu/value.nim +++ b/src/kuzu/value.nim @@ -131,6 +131,23 @@ func toList*( value: KuzuValue ): seq[ KuzuValue ] = const toSeq* = toList +proc toBlob*( value: KuzuValue ): seq[ byte ] = + ## Conversion from Kuzu type to Nim - returns a BLOB as a sequence of bytes. + if value.kind != KUZU_BLOB: + raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != blob" ) + + result = @[] + var data: ptr byte + assert( kuzu_value_get_blob( addr value.handle, addr data ) == KuzuSuccess ) + + for idx in 0 .. BLOB_MAXSIZE: + var byte = cast[ptr byte](cast[uint](data) + idx.uint)[] + if byte == 0: break + result.add( byte ) + + kuzu_destroy_blob( data ) + + func toStruct*( value: KuzuValue ): KuzuStructValue = ## Create a convenience class for struct-like KuzuValues. if not [ @@ -186,20 +203,3 @@ func `$`*( struct: KuzuStructValue ): string = result = $kuzu_value_to_string( addr struct.value.handle ) -proc toBlob*( value: KuzuValue ): seq[ byte ] = - ## Conversion from Kuzu type to Nim - returns a BLOB as a sequence of bytes. - if value.kind != KUZU_BLOB: - raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != blob" ) - - result = @[] - var data: ptr byte - assert( kuzu_value_get_blob( addr value.handle, addr data ) == KuzuSuccess ) - - for idx in 0 .. BLOB_MAXSIZE: - var byte = cast[ptr byte](cast[uint](data) + idx.uint)[] - if byte == 0: break - result.add( byte ) - - kuzu_destroy_blob( data ) - - diff --git a/tests/values/t_can_return_a_blob_object.nim b/tests/values/t_can_return_a_blob_object.nim new file mode 100644 index 0000000..4b3ee81 --- /dev/null +++ b/tests/values/t_can_return_a_blob_object.nim @@ -0,0 +1,32 @@ +# vim: set et sta sw=4 ts=4 : + +import kuzu + +var db = newKuzuDatabase() +var conn = db.connect + +var q = conn.query( "CREATE NODE TABLE Doot ( id SERIAL, data BLOB, PRIMARY KEY(id) )" ) + +# (188, 189, 186, 170) +q = conn.query( """CREATE (d:Doot {data: BLOB('\\xBC\\xBD\\xBA\\xAA')})""" ) + +var stmt = conn.prepare( "CREATE (d:Doot {data: encode($str)})" ) +q = stmt.execute( (str: "Hello!") ) +q = conn.query( "MATCH (d:Doot) RETURN d.data" ) + +var expected: seq[byte] = @[188, 189, 186, 170] +var val = q.getNext[0] +assert val.kind == KUZU_BLOB +assert val.toBlob == expected + +expected = @[72, 101, 108, 108, 111, 33] +val = q.getNext[0] +assert val.kind == KUZU_BLOB +assert val.toBlob == expected + +var str: string +for c in expected: + str.add( c.char ) + +assert str == "Hello!" +