Add first round of kuzu -> nim type conversions.
FossilOrigin-Name: 7435f8dcdff8f0a22eb1a07b4e19f379dd731e6557eeefb5dbf75f5b32cb82ed
This commit is contained in:
parent
db85c36d70
commit
6f6ab2f85a
8 changed files with 392 additions and 9 deletions
|
|
@ -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).)" )
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
]#
|
||||||
|
|
||||||
|
|
|
||||||
96
tests/values/t_can_convert_to_native_types.nim
Normal file
96
tests/values/t_can_convert_to_native_types.nim
Normal 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."
|
||||||
|
|
||||||
39
tests/values/t_can_return_a_list_object.nim
Normal file
39
tests/values/t_can_return_a_list_object.nim
Normal 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]
|
||||||
|
|
||||||
22
tests/values/t_can_return_a_struct_object.nim
Normal file
22
tests/values/t_can_return_a_struct_object.nim
Normal 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"
|
||||||
|
|
||||||
24
tests/values/t_raise_error_on_invalid_conversion.nim
Normal file
24
tests/values/t_raise_error_on_invalid_conversion.nim
Normal 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""" )
|
||||||
|
|
||||||
|
|
||||||
21
tests/values/t_raises_error_on_struct_missing_key.nim
Normal file
21
tests/values/t_raises_error_on_struct_missing_key.nim
Normal 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"""" )
|
||||||
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue