Add first round of kuzu -> nim type conversions.

FossilOrigin-Name: 7435f8dcdff8f0a22eb1a07b4e19f379dd731e6557eeefb5dbf75f5b32cb82ed
This commit is contained in:
mahlon 2025-03-30 10:03:39 +00:00
parent db85c36d70
commit 6f6ab2f85a
8 changed files with 392 additions and 9 deletions

View file

@ -1,5 +1,7 @@
# vim: set et sta sw=4 ts=4 : # vim: set et sta sw=4 ts=4 :
# NOTE: Constructor in queries.nim, #getNext
proc `=destroy`*( tpl: KuzuFlatTupleObj ) = proc `=destroy`*( tpl: KuzuFlatTupleObj ) =
## Graceful cleanup for out of scope tuples. ## Graceful cleanup for out of scope tuples.
if tpl.valid: if tpl.valid:
@ -25,3 +27,4 @@ func `[]`*( tpl: KuzuFlatTuple, idx: int ): KuzuValue =
raise newException( KuzuIndexError, raise newException( KuzuIndexError,
&"Unable to fetch tuple value at idx {idx}. ({tpl.num_columns} column(s).)" ) &"Unable to fetch tuple value at idx {idx}. ({tpl.num_columns} column(s).)" )

View file

@ -42,6 +42,12 @@ type
kind*: kuzu_data_type_id kind*: kuzu_data_type_id
KuzuValue* = ref KuzuValueObj KuzuValue* = ref KuzuValueObj
KuzuStructValueObj = object
value: KuzuValue
len*: uint64
keys*: seq[ string ]
KuzuStructValue* = ref KuzuStructValueObj
KuzuException* = object of CatchableError KuzuException* = object of CatchableError
KuzuQueryError* = object of KuzuException KuzuQueryError* = object of KuzuException
KuzuIndexError* = object of KuzuException KuzuIndexError* = object of KuzuException

View file

@ -1,5 +1,7 @@
# vim: set et sta sw=4 ts=4 : # vim: set et sta sw=4 ts=4 :
# NOTE: Constructor in tuples.nim, #[]
proc `=destroy`*( value: KuzuValueObj ) = proc `=destroy`*( value: KuzuValueObj ) =
## Graceful cleanup for out of scope values. ## Graceful cleanup for out of scope values.
if value.valid: if value.valid:
@ -7,11 +9,6 @@ proc `=destroy`*( value: KuzuValueObj ) =
kuzu_value_destroy( addr value.handle ) kuzu_value_destroy( addr value.handle )
func `$`*( value: KuzuValue ): string =
## Stringify a value.
result = $kuzu_value_to_string( addr value.handle )
func getType( value: KuzuValue ) = func getType( value: KuzuValue ) =
## Find and set the native Kuzu type of this value. ## Find and set the native Kuzu type of this value.
var logical_type: kuzu_logical_type var logical_type: kuzu_logical_type
@ -20,11 +17,186 @@ func getType( value: KuzuValue ) =
kuzu_data_type_destroy( addr logical_type ) kuzu_data_type_destroy( addr logical_type )
func `$`*( value: KuzuValue ): string =
## Stringify a value.
result = $kuzu_value_to_string( addr value.handle )
func toBool*( value: KuzuValue ): bool =
## Conversion from Kuzu type to Nim.
if value.kind != KUZU_BOOL:
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != bool" )
assert( kuzu_value_get_bool( addr value.handle, addr result ) == KuzuSuccess )
func toInt8*( value: KuzuValue ): int8 = func toInt8*( value: KuzuValue ): int8 =
## Conversion from Kuzu type to Nim.
if value.kind != KUZU_INT8: if value.kind != KUZU_INT8:
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != int8" ) raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != int8" )
assert( assert( kuzu_value_get_int8( addr value.handle, addr result ) == KuzuSuccess )
kuzu_value_get_int8( addr value.handle, addr result ) ==
KuzuSuccess
) func toInt16*( value: KuzuValue ): int16 =
## Conversion from Kuzu type to Nim.
if value.kind != KUZU_INT16:
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != int16" )
assert( kuzu_value_get_int16( addr value.handle, addr result ) == KuzuSuccess )
func toInt32*( value: KuzuValue ): int32 =
## Conversion from Kuzu type to Nim.
if value.kind != KUZU_INT32:
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != int32" )
assert( kuzu_value_get_int32( addr value.handle, addr result ) == KuzuSuccess )
func toInt64*( value: KuzuValue ): int64 =
## Conversion from Kuzu type to Nim.
if value.kind != KUZU_INT64:
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != int64" )
assert( kuzu_value_get_int64( addr value.handle, addr result ) == KuzuSuccess )
func toUint8*( value: KuzuValue ): uint8 =
## Conversion from Kuzu type to Nim.
if value.kind != KUZU_UINT8:
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != uint8" )
assert( kuzu_value_get_uint8( addr value.handle, addr result ) == KuzuSuccess )
func toUint16*( value: KuzuValue ): uint16 =
## Conversion from Kuzu type to Nim.
if value.kind != KUZU_UINT16:
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != uint16" )
assert( kuzu_value_get_uint16( addr value.handle, addr result ) == KuzuSuccess )
func toUint32*( value: KuzuValue ): uint32 =
## Conversion from Kuzu type to Nim.
if value.kind != KUZU_UINT32:
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != uint32" )
assert( kuzu_value_get_uint32( addr value.handle, addr result ) == KuzuSuccess )
func toUint64*( value: KuzuValue ): uint64 =
## Conversion from Kuzu type to Nim.
if value.kind != KUZU_UINT64:
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != uint64" )
assert( kuzu_value_get_uint64( addr value.handle, addr result ) == KuzuSuccess )
func toDouble*( value: KuzuValue ): float =
## Conversion from Kuzu type to Nim.
if value.kind != KUZU_DOUBLE:
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != double" )
assert( kuzu_value_get_double( addr value.handle, addr result ) == KuzuSuccess )
func toFloat*( value: KuzuValue ): float =
## Conversion from Kuzu type to Nim.
if value.kind != KUZU_FLOAT:
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != float" )
var rv: cfloat
assert( kuzu_value_get_float( addr value.handle, addr rv ) == KuzuSuccess )
result = rv
func toTimestamp*( value: KuzuValue ): int =
## Conversion from Kuzu type to Nim.
if value.kind != KUZU_TIMESTAMP:
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != timestamp" )
var rv: kuzu_timestamp_t
assert( kuzu_value_get_timestamp( addr value.handle, addr rv ) == KuzuSuccess )
result = rv.value
func toList*( value: KuzuValue ): seq[ KuzuValue ] =
## Return a sequence from KUZU_LIST values.
if value.kind != KUZU_LIST:
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != list" )
result = @[]
var size: uint64
assert( kuzu_value_get_list_size( addr value.handle, addr size ) == KuzuSuccess )
if size == 0: return
for i in ( 0 .. size-1 ):
var kval = new KuzuValue
assert(
kuzu_value_get_list_element(
addr value.handle, i.uint64, addr kval.handle
) == KuzuSuccess )
kval.getType()
result.add( kval )
const toSeq* = toList
func toStruct*( value: KuzuValue ): KuzuStructValue =
## Create a convenience class for struct-like KuzuValues.
if not [
KUZU_STRUCT,
KUZU_NODE,
KUZU_REL,
KUZU_RECURSIVE_REL,
KUZU_UNION
].contains( value.kind ):
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != struct" )
result = new KuzuStructValue
result.value = value
discard kuzu_value_get_struct_num_fields( addr value.handle, addr result.len )
if result.len == 0: return
# Build keys
for idx in ( 0 .. result.len - 1 ):
var keyname: cstring
assert(
kuzu_value_get_struct_field_name(
addr value.handle, idx.uint64, addr keyname
) == KuzuSuccess )
result.keys.add( $keyname )
const toNode* = toStruct
const toRel* = toStruct
func `[]`*( struct: KuzuStructValue, key: string ): KuzuValue =
## Return a KuzuValue for the struct *key*.
var idx: uint64
var found = false
for i in ( 0 .. struct.len-1 ):
if struct.keys[i] == key:
found = true
idx = i
break
if not found:
raise newException( KuzuIndexError,
&"""No such struct key "{key}".""" )
result = new KuzuValue
assert(
kuzu_value_get_struct_field_value(
addr struct.value.handle, idx.uint64, addr result.handle
) == KuzuSuccess )
result.getType()
func `$`*( struct: KuzuStructValue ): string =
## Stringify a struct value.
result = $kuzu_value_to_string( addr struct.value.handle )
# func toBlob*( value: KuzuValue ): array[ 4096, byte ] =
# ## Conversion from Kuzu type to Nim.
# if value.kind != KUZU_BLOB:
# raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != blob" )
# var rv: array[ 4096, byte ]
# assert( kuzu_value_get_blob( addr value.handle, addr rv[0] ) == KuzuSuccess )
# return rv
#[
BLOB
MAP
]#

View file

@ -0,0 +1,96 @@
# vim: set et sta sw=4 ts=4 :
import kuzu
let db = newKuzuDatabase()
let conn = db.connect
var q = conn.query( """CREATE NODE TABLE Doop (
id SERIAL,
test1 int8,
test2 int16,
test3 int32,
test4 int64,
test5 uint8,
test6 uint16,
test7 uint32,
test8 uint64,
test9 float,
test10 double,
test11 timestamp,
test12 string,
PRIMARY KEY(id))""" )
q = conn.query( """CREATE (d:Doop {
test1: 12,
test2: 144,
test3: -2201,
test4: 123550332,
test5: 2,
test6: 82,
test7: 50922,
test8: 294050066922312345,
test9: 10.5,
test10: 2.333021,
test11: TIMESTAMP("2025-03-29"),
test12: "Well hello, there." })""" )
q = conn.query( "MATCH (d:Doop) RETURN d.*" )
var tup = q.getNext
var id = tup[0].toInt64
assert typeOf( id ) is int64
assert id == 0
var test1 = tup[1].toInt8
assert typeOf( test1 ) is int8
assert test1 == 12
var test2 = tup[2].toInt16
assert typeOf( test2 ) is int16
assert test2 == 144
var test3 = tup[3].toInt32
assert typeOf( test3 ) is int32
assert test3 == -2201
var test4 = tup[4].toInt64
assert typeOf( test4 ) is int64
assert test4 == 123550332
var test5 = tup[5].toUint8
assert typeOf( test5 ) is uint8
assert test5 == 2
var test6 = tup[6].toUint16
assert typeOf( test6 ) is uint16
assert test6 == 82
var test7 = tup[7].toUint32
assert typeOf( test7 ) is uint32
assert test7 == 50922
var test8 = tup[8].toUint64
assert typeOf( test8 ) is uint64
assert test8 == 294050066922312345.uint64
var test9 = tup[9].toFloat
assert typeOf( test9 ) is float
assert test9 == 10.5
var test10 = tup[10].toDouble
assert typeOf( test10 ) is float
assert test10 == 2.333021
var test11 = tup[11].toTimestamp
assert typeOf( test11 ) is int
assert test11 == 1743206400000000
var test11s = $tup[11]
assert typeOf( test11s ) is string
assert test11s == "2025-03-29 00:00:00"
var test12 = $tup[12]
assert typeOf( test12 ) is string
assert test12 == "Well hello, there."

View file

@ -0,0 +1,39 @@
# vim: set et sta sw=4 ts=4 :
import kuzu
let db = newKuzuDatabase()
let conn = db.connect
var q = conn.query( "RETURN [1,2,3,4,5] AS list" )
var list = q.getNext[0]
assert list.kind == KUZU_LIST
var items = list.toSeq
assert items.len == 5
assert typeOf( items ) is seq[KuzuValue]
for i in items:
assert( i.kind == KUZU_INT64 )
q = conn.query( """RETURN ["woo", "hoo"] AS list""" )
list = q.getNext[0]
assert list.kind == KUZU_LIST
items = list.toSeq
assert items.len == 2
assert typeOf( items ) is seq[KuzuValue]
for i in items:
assert( i.kind == KUZU_STRING )
q = conn.query( """RETURN [] AS list""" )
list = q.getNext[0]
assert list.kind == KUZU_LIST
items = list.toList
assert items.len == 0
assert typeOf( items ) is seq[KuzuValue]

View file

@ -0,0 +1,22 @@
# vim: set et sta sw=4 ts=4 :
import kuzu
let db = newKuzuDatabase()
let conn = db.connect
var q = conn.query( """RETURN 12""" )
try:
discard q.getNext[0].toStruct
except KuzuTypeError:
discard
q = conn.query( """RETURN {test1: 1, test2: "bewts"} AS struct""" )
var struct = q.getNext[0].toStruct
assert struct.len == 2
assert struct.keys == @["test1", "test2"]
assert struct["test1"].toInt64 == 1
assert $struct["test2"] == "bewts"

View file

@ -0,0 +1,24 @@
# vim: set et sta sw=4 ts=4 :
import
std/re
import kuzu
let db = newKuzuDatabase()
let conn = db.connect
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
q = conn.query( "CREATE (d:Doop {thing: 'okay!'})" )
q = conn.query( "MATCH (d:Doop) RETURN d" )
var tup = q.getNext
var val = tup[0]
assert val.kind == KUZU_NODE
try:
discard val.toInt32
except KuzuTypeError as err:
assert err.msg.contains( re"""Mismatched types: KUZU_NODE != int32""" )

View file

@ -0,0 +1,21 @@
# vim: set et sta sw=4 ts=4 :
import
std/re
import kuzu
let db = newKuzuDatabase()
let conn = db.connect
var q = conn.query( """RETURN {test1: 1, test2: "bewts"} AS struct""" )
var struct = q.getNext[0].toStruct
assert struct.len == 2
assert struct.keys == @["test1", "test2"]
try:
discard struct["nope"]
except KuzuIndexError as err:
assert err.msg.contains( re"""No such struct key "nope"""" )