Multiple changes.

- Minor README updates.
- Create LICENSE and History files.
- Use 'func' instead of 'proc' where applicable.
- Add some destructor debug.
- Rename primary exceptions to 'X-error'.
- Bind to proper object types in prepared statement parameters.
- Retain the found 'type' in the KuzuValue object.

FossilOrigin-Name: db59c0b901b1715170e0d269fc2bf00477ac48af4d10a747eb5a749adbf6268e
This commit is contained in:
mahlon 2025-03-29 23:17:10 +00:00
parent 421cb87e57
commit db85c36d70
22 changed files with 284 additions and 162 deletions

15
.editorconfig Normal file
View file

@ -0,0 +1,15 @@
# http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Space indentation
[**.nim]
indent_style = space
indent_size = 4

7
History.md Normal file
View file

@ -0,0 +1,7 @@
# Release History for nim-kuzu
---
## v0.1.0 [2025-??-??] Mahlon E. Smith <mahlon@martini.nu>
Initial public release.

29
LICENCE Normal file
View file

@ -0,0 +1,29 @@
Copyright (c) 2025 Mahlon E. Smith
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author/s, nor the names of the project's
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -10,7 +10,7 @@ github_mirror
## Description
This is a Nim binding for the Kuzu graph database library.
This is a Nim binding for the [Kuzu](https://kuzudb.com) graph database library.
Kuzu is an embedded graph database built for query speed and scalability. It is
optimized for handling complex join-heavy analytical workloads on very large
@ -32,7 +32,7 @@ For more information about Kuzu itself, see its
## Prerequisites
* A functioning Nim >= 2 installation
- [KuzuDB](https://kuzudb.com)
- [KuzuDB](https://kuzudb.com) to be locally installed!
## Installation
@ -76,34 +76,3 @@ development.
A note of thanks to @mantielero on Github, who has a Kuzu binding for an early
KuzuDB (0.4.x) that I found after starting this project.
## License
Copyright (c) 2025 Mahlon E. Smith
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author/s, nor the names of the project's
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,6 +1,6 @@
# vim: set et sta sw=4 ts=4 :
proc kuzuConfig*(
func kuzuConfig*(
buffer_pool_size = KUZU_DEFAULT_CONFIG.buffer_pool_size,
max_num_threads = KUZU_DEFAULT_CONFIG.max_num_threads,
enable_compression = KUZU_DEFAULT_CONFIG.enable_compression,

View file

@ -3,10 +3,11 @@
proc `=destroy`*( conn: KuzuConnectionObj ) =
## Graceful cleanup for open connection handles.
if conn.valid:
when defined( debug ): echo &"Destroying connection: {conn}"
kuzu_connection_destroy( addr conn.handle )
proc connect*( db: KuzuDatabase ): KuzuConnection =
func connect*( db: KuzuDatabase ): KuzuConnection =
## Connect to a database.
result = new KuzuConnection
if kuzu_connection_init( addr db.handle, addr result.handle ) == KuzuSuccess:
@ -15,12 +16,12 @@ proc connect*( db: KuzuDatabase ): KuzuConnection =
raise newException( KuzuException, "Unable to connect to the database." )
proc queryTimeout*( conn: KuzuConnection, timeout: uint64 ) =
func queryTimeout*( conn: KuzuConnection, timeout: uint64 ) =
## Set a maximum time limit (in milliseconds) for query runtime.
discard kuzu_connection_set_query_timeout( addr conn.handle, timeout )
proc queryInterrupt*( conn: KuzuConnection ) =
func queryInterrupt*( conn: KuzuConnection ) =
## Cancel any running queries.
kuzu_connection_interrupt( addr conn.handle )

View file

@ -3,10 +3,11 @@
proc `=destroy`*( db: KuzuDatabaseObj ) =
## Graceful cleanup for an open DB handle when it goes out of scope.
if db.valid:
when defined( debug ): echo &"Destroying database: {db}"
kuzu_database_destroy( addr db.handle )
proc newKuzuDatabase*( path="", config=kuzuConfig() ): KuzuDatabase =
func newKuzuDatabase*( path="", config=kuzuConfig() ): KuzuDatabase =
## Create a new Kuzu database handle. Creates an in-memory
## database by default, but writes to disk if a +path+ is supplied.

View file

@ -3,10 +3,11 @@
proc `=destroy`*( query: KuzuQueryResultObj ) =
## Graceful cleanup for out of scope query objects.
if query.valid:
when defined( debug ): echo &"Destroying query: {query}"
kuzu_query_result_destroy( addr query.handle )
proc getQueryMetadata( query: KuzuQueryResult ) =
func getQueryMetadata( query: KuzuQueryResult ) =
## Find and retain additional data for the query.
query.num_columns = kuzu_query_result_get_num_columns( addr query.handle )
query.num_tuples = kuzu_query_result_get_num_tuples( addr query.handle )
@ -21,6 +22,7 @@ proc getQueryMetadata( query: KuzuQueryResult ) =
# Column information.
query.column_types = @[]
query.column_names = @[]
if query.num_columns == 0: return
for idx in ( 0 .. query.num_columns-1 ):
# types
@ -46,7 +48,7 @@ proc getQueryMetadata( query: KuzuQueryResult ) =
kuzu_destroy_string( name )
proc query*( conn: KuzuConnection, query: string ): KuzuQueryResult =
func query*( conn: KuzuConnection, query: string ): KuzuQueryResult =
## Perform a database +query+ and return the result.
result = new KuzuQueryResult
@ -55,16 +57,17 @@ proc query*( conn: KuzuConnection, query: string ): KuzuQueryResult =
result.getQueryMetadata()
else:
var err = kuzu_query_result_get_error_message( addr result.handle )
raise newException( KuzuQueryException, &"Error running query: {err}" )
raise newException( KuzuQueryError, &"Error running query: {err}" )
proc `=destroy`*( prepared: KuzuPreparedStatementObj ) =
## Graceful cleanup for out of scope prepared objects.
if prepared.valid:
when defined( debug ): echo &"Destroying prepared statement: {prepared}"
kuzu_prepared_statement_destroy( addr prepared.handle )
proc prepare*( conn: KuzuConnection, query: string ): KuzuPreparedStatement =
func prepare*( conn: KuzuConnection, query: string ): KuzuPreparedStatement =
## Return a prepared statement that can avoid planning for repeat calls,
## with optional variable binding via #execute.
result = new KuzuPreparedStatement
@ -73,7 +76,40 @@ proc prepare*( conn: KuzuConnection, query: string ): KuzuPreparedStatement =
result.valid = true
else:
var err = kuzu_prepared_statement_get_error_message( addr result.handle )
raise newException( KuzuQueryException, &"Error preparing statement: {err}" )
raise newException( KuzuQueryError, &"Error preparing statement: {err}" )
func bindValue[T](
stmtHandle: kuzu_prepared_statement,
key: cstring,
val: T
) =
## Bind a key/value to a prepared statement handle.
when typeOf( val ) is bool:
assert( kuzu_prepared_statement_bind_bool( addr stmtHandle, key, val ) == KuzuSuccess )
elif typeOf( val ) is int8:
assert( kuzu_prepared_statement_bind_int8( addr stmtHandle, key, val ) == KuzuSuccess )
elif typeOf( val ) is int16:
assert( kuzu_prepared_statement_bind_int16( addr stmtHandle, key, val ) == KuzuSuccess )
elif typeOf( val ) is int64:
assert( kuzu_prepared_statement_bind_int64( addr stmtHandle, key, val ) == KuzuSuccess )
elif typeOf( val ) is int or typeOf( val ) is int32:
assert( kuzu_prepared_statement_bind_int32( addr stmtHandle, key, val.int32 ) == KuzuSuccess )
elif typeOf( val ) is uint8:
assert( kuzu_prepared_statement_bind_uint8( addr stmtHandle, key, val ) == KuzuSuccess )
elif typeOf( val ) is uint16:
assert( kuzu_prepared_statement_bind_uint16( addr stmtHandle, key, val ) == KuzuSuccess )
elif typeOf( val ) is uint64:
assert( kuzu_prepared_statement_bind_uint64( addr stmtHandle, key, val ) == KuzuSuccess )
elif typeOf( val ) is uint or typeOf( val ) is uint32:
assert( kuzu_prepared_statement_bind_uint32( addr stmtHandle, key, val.uint32 ) == KuzuSuccess )
elif typeOf( val ) is float:
assert( kuzu_prepared_statement_bind_double( addr stmtHandle, key, val ) == KuzuSuccess )
elif typeOf( val ) is string:
# Fallback to string. For custom types, just cast in the cypher query.
assert( kuzu_prepared_statement_bind_string( addr stmtHandle, key, val.cstring ) == KuzuSuccess )
else:
raise newException( KuzuTypeError, &"""Unsupported type {$typeOf(val)} for prepared statement.""" )
proc execute*(
@ -86,111 +122,42 @@ proc execute*(
result = new KuzuQueryResult
for key, val in params.fieldPairs:
#
# FIXME: type checks and conversions for all bound variables
# from nim types to supported Kuzu types.
#
discard kuzu_prepared_statement_bind_string( addr prepared.handle, key.cstring, val.cstring )
#[
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_bool (kuzu_prepared_statement *prepared_statement, const char *param_name, bool value)
Binds the given boolean value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_int64 (kuzu_prepared_statement *prepared_statement, const char *param_name, int64_t value)
Binds the given int64_t value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_int32 (kuzu_prepared_statement *prepared_statement, const char *param_name, int32_t value)
Binds the given int32_t value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_int16 (kuzu_prepared_statement *prepared_statement, const char *param_name, int16_t value)
Binds the given int16_t value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_int8 (kuzu_prepared_statement *prepared_statement, const char *param_name, int8_t value)
Binds the given int8_t value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_uint64 (kuzu_prepared_statement *prepared_statement, const char *param_name, uint64_t value)
Binds the given uint64_t value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_uint32 (kuzu_prepared_statement *prepared_statement, const char *param_name, uint32_t value)
Binds the given uint32_t value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_uint16 (kuzu_prepared_statement *prepared_statement, const char *param_name, uint16_t value)
Binds the given uint16_t value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_uint8 (kuzu_prepared_statement *prepared_statement, const char *param_name, uint8_t value)
Binds the given int8_t value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_double (kuzu_prepared_statement *prepared_statement, const char *param_name, double value)
Binds the given double value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_float (kuzu_prepared_statement *prepared_statement, const char *param_name, float value)
Binds the given float value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_date (kuzu_prepared_statement *prepared_statement, const char *param_name, kuzu_date_t value)
Binds the given date value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_timestamp_ns (kuzu_prepared_statement *prepared_statement, const char *param_name, kuzu_timestamp_ns_t value)
Binds the given timestamp_ns value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_timestamp_sec (kuzu_prepared_statement *prepared_statement, const char *param_name, kuzu_timestamp_sec_t value)
Binds the given timestamp_sec value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_timestamp_tz (kuzu_prepared_statement *prepared_statement, const char *param_name, kuzu_timestamp_tz_t value)
Binds the given timestamp_tz value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_timestamp_ms (kuzu_prepared_statement *prepared_statement, const char *param_name, kuzu_timestamp_ms_t value)
Binds the given timestamp_ms value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_timestamp (kuzu_prepared_statement *prepared_statement, const char *param_name, kuzu_timestamp_t value)
Binds the given timestamp value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_interval (kuzu_prepared_statement *prepared_statement, const char *param_name, kuzu_interval_t value)
Binds the given interval value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_string (kuzu_prepared_statement *prepared_statement, const char *param_name, const char *value)
Binds the given string value to the given parameter name in the prepared statement.
KUZU_C_API kuzu_state kuzu_prepared_statement_bind_value (kuzu_prepared_statement *prepared_statement, const char *param_name, kuzu_value *value)
]#
prepared.handle.bindValue( key, val )
if kuzu_connection_execute(
addr prepared.conn.handle,
addr prepared.handle,
addr result.handle
) == KuzuSuccess:
discard kuzu_query_result_get_query_summary( addr result.handle, addr result.summary )
result.num_columns = kuzu_query_result_get_num_columns( addr result.handle )
result.num_tuples = kuzu_query_result_get_num_tuples( addr result.handle )
result.compile_time = kuzu_query_summary_get_compiling_time( addr result.summary )
result.execution_time = kuzu_query_summary_get_execution_time( addr result.summary )
result.valid = true
result.valid = false
result.getQueryMetadata()
else:
var err = kuzu_query_result_get_error_message( addr result.handle )
raise newException( KuzuQueryException, &"Error executing prepared statement: {err}" )
raise newException( KuzuQueryError, &"Error executing prepared statement: {err}" )
proc `$`*( query: KuzuQueryResult ): string =
func `$`*( query: KuzuQueryResult ): string =
## Return the entire result set as a string.
result = $kuzu_query_result_to_string( addr query.handle )
proc hasNext*( query: KuzuQueryResult ): bool =
func hasNext*( query: KuzuQueryResult ): bool =
## Returns +true+ if there are more tuples to be consumed.
result = kuzu_query_result_has_next( addr query.handle )
proc getNext*( query: KuzuQueryResult ): KuzuFlatTuple =
## Consume and return the next tuple result, or raise a KuzuIndexException
func getNext*( query: KuzuQueryResult ): KuzuFlatTuple =
## Consume and return the next tuple result, or raise a KuzuIndexError
## if at the end of the result set.
result = new KuzuFlatTuple
if kuzu_query_result_get_next( addr query.handle, addr result.handle ) == KuzuSuccess:
result.valid = true
result.num_columns = query.num_columns
else:
raise newException( KuzuIndexException, &"Query iteration past end." )
raise newException( KuzuIndexError, &"Query iteration past end." )
proc rewind*( query: KuzuQueryResult ) =
func rewind*( query: KuzuQueryResult ) =
## Reset query iteration back to the beginning.
kuzu_query_result_reset_iterator( addr query.handle )

View file

@ -3,22 +3,25 @@
proc `=destroy`*( tpl: KuzuFlatTupleObj ) =
## Graceful cleanup for out of scope tuples.
if tpl.valid:
when defined( debug ): echo &"Destroying tuple: {tpl}"
kuzu_flat_tuple_destroy( addr tpl.handle )
proc `$`*( tpl: KuzuFlatTuple ): string =
func `$`*( tpl: KuzuFlatTuple ): string =
## Stringify a tuple.
result = $kuzu_flat_tuple_to_string( addr tpl.handle )
result.removeSuffix( "\n" )
proc `[]`*( tpl: KuzuFlatTuple, idx: int ): KuzuValue =
## Returns a KuzuValue at the given +idx+.
func `[]`*( tpl: KuzuFlatTuple, idx: int ): KuzuValue =
## Returns a KuzuValue at the given *idx*.
result = new KuzuValue
if kuzu_flat_tuple_get_value( addr tpl.handle, idx.uint64, addr result.handle ) == KuzuSuccess:
result.valid = true
result.getType()
else:
raise newException( KuzuIndexException,
raise newException( KuzuIndexError,
&"Unable to fetch tuple value at idx {idx}. ({tpl.num_columns} column(s).)" )

View file

@ -39,9 +39,11 @@ type
KuzuValueObj = object
handle: kuzu_value
valid = false
kind*: kuzu_data_type_id
KuzuValue* = ref KuzuValueObj
KuzuException* = object of CatchableError
KuzuQueryException* = object of KuzuException
KuzuIndexException* = object of KuzuException
KuzuQueryError* = object of KuzuException
KuzuIndexError* = object of KuzuException
KuzuTypeError* = object of KuzuException

View file

@ -3,41 +3,28 @@
proc `=destroy`*( value: KuzuValueObj ) =
## Graceful cleanup for out of scope values.
if value.valid:
when defined( debug ): echo &"Destroying value: {value}"
kuzu_value_destroy( addr value.handle )
proc `$`*( value: KuzuValue ): string =
func `$`*( value: KuzuValue ): string =
## Stringify a value.
result = $kuzu_value_to_string( addr value.handle )
proc kind*( value: KuzuValue ): kuzu_data_type_id =
## Find and return the native Kuzu type of this value.
func getType( value: KuzuValue ) =
## Find and set the native Kuzu type of this value.
var logical_type: kuzu_logical_type
kuzu_value_get_data_type( addr value.handle, addr logical_type )
result = kuzu_data_type_get_id( addr logical_type )
# var num: uint64
# discard kuzu_data_type_get_num_elements_in_array( addr logical_type, addr num )
# echo "HMMM ", $num
value.kind = kuzu_data_type_get_id( addr logical_type )
kuzu_data_type_destroy( addr logical_type )
# enum_kuzu_data_type_id_570425857* {.size: sizeof(cuint).} = enum
# KUZU_ANY = 0, KUZU_NODE = 10, KUZU_REL = 11, KUZU_RECURSIVE_REL = 12,
# KUZU_SERIAL = 13, KUZU_BOOL = 22, KUZU_INT64 = 23, KUZU_INT32 = 24,
# KUZU_INT16 = 25, KUZU_INT8 = 26, KUZU_UINT64 = 27, KUZU_UINT32 = 28,
# KUZU_UINT16 = 29, KUZU_UINT8 = 30, KUZU_INT128 = 31, KUZU_DOUBLE = 32,
# KUZU_FLOAT = 33, KUZU_DATE = 34, KUZU_TIMESTAMP = 35,
# KUZU_TIMESTAMP_SEC = 36, KUZU_TIMESTAMP_MS = 37, KUZU_TIMESTAMP_NS = 38,
# KUZU_TIMESTAMP_TZ = 39, KUZU_INTERVAL = 40, KUZU_DECIMAL = 41,
# KUZU_INTERNAL_ID = 42, KUZU_STRING = 50, KUZU_BLOB = 51, KUZU_LIST = 52,
# KUZU_ARRAY = 53, KUZU_STRUCT = 54, KUZU_MAP = 55, KUZU_UNION = 56,
# KUZU_POINTER = 58, KUZU_UUID = 59
# proc getValue*( value: KuzuValue ):
#
# FIXME: type checks and conversions from supported kuzu
# types to supported Nim types.
#
# Currently the value can only be stringified via `$`.
#
func toInt8*( value: KuzuValue ): int8 =
if value.kind != KUZU_INT8:
raise newException( KuzuTypeError, &"Mismatched types: {value.kind} != int8" )
assert(
kuzu_value_get_int8( addr value.handle, addr result ) ==
KuzuSuccess
)

View file

@ -0,0 +1,55 @@
# vim: set et sta sw=4 ts=4 :
discard """
output: "0|-222222|128|True|Stuff!|3.344903|239.299923|a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11|2025-03-29"
"""
import kuzu
let db = newKuzuDatabase()
let conn = db.connect
var q = conn.query( """CREATE NODE TABLE Doop (
id SERIAL,
num INT,
unum UINT8,
woo BOOL,
thing STRING,
float FLOAT,
double DOUBLE,
uuid UUID,
date DATE,
PRIMARY KEY(id)
)""" )
assert typeOf( q ) is KuzuQueryResult
var stmt = conn.prepare( """CREATE (d:Doop {
woo: $woo,
thing: $thing,
num: $num,
unum: $unum,
float: $float,
double: $double,
uuid: UUID($uuid),
date: DATE($date)
})""" )
assert typeOf( stmt ) is KuzuPreparedStatement
q = stmt.execute((
woo: true,
thing: "Stuff!",
num: -222222,
unum: 128,
float: 3.34490345039450345,
double: 239.299922883992,
uuid: "A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11",
date: "2025-03-29"
))
assert typeOf( q ) is KuzuQueryResult
q = conn.query( "MATCH (d:Doop) RETURN d.*" )
echo $q.getNext

View file

@ -0,0 +1,17 @@
# 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, thing STRING, PRIMARY KEY(id) )" )
assert typeOf( q ) is KuzuQueryResult
q = conn.query( "CREATE (d:Doop {thing: 'okay!'})" )
q = conn.query( "MATCH (d:Doop) RETURN d.id AS IDENTIFIER, d.thing AS THING" )
assert q.column_names.len == 2
assert q.column_names[0] == "IDENTIFIER"
assert q.column_names[1] == "THING"

View file

@ -0,0 +1,17 @@
# 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, thing STRING, PRIMARY KEY(id) )" )
assert typeOf( q ) is KuzuQueryResult
q = conn.query( "CREATE (d:Doop {thing: 'okay!'})" )
q = conn.query( "MATCH (d:Doop) RETURN d.id AS IDENTIFIER, d.thing AS THING" )
assert q.column_types.len == 2
assert $q.column_types[0] == "KUZU_SERIAL"
assert $q.column_types[1] == "KUZU_STRING"

View file

@ -9,6 +9,6 @@ let conn = db.connect
try:
discard conn.query( "NOPE NOPE NOPE" )
except KuzuQueryException as err:
assert err.msg.contains( re"""Error running query:.*extraneous input 'NOPE'""" )
except KuzuQueryError as err:
assert err.msg.contains( re"""Parser exception: extraneous input 'NOPE'""" )

View file

@ -0,0 +1,31 @@
# 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, created DATE, PRIMARY KEY(id) )" )
assert typeOf( q ) is KuzuQueryResult
var p = conn.prepare( "CREATE (d:Doop {created: $created})" )
assert typeOf( p ) is KuzuPreparedStatement
# Typecast binding failure
#
try:
discard p.execute( (created: "1111-1111") )
except KuzuQueryError as err:
assert err.msg.contains( re"""Expression \$created has data type STRING but expected DATE.""" )
# Invalid value for typecast
#
p = conn.prepare( "CREATE (d:Doop {created: DATE($created)})" )
try:
discard p.execute( (created: "1111-1111") )
except KuzuQueryError as err:
assert err.msg.contains( re"""Given: "1111-1111". Expected format: \(YYYY-MM-DD\)""" )

View file

@ -14,7 +14,7 @@ assert typeOf( p ) is KuzuPreparedStatement
try:
discard p.execute( (nope: "undefined var in statement!") )
except KuzuQueryException as err:
except KuzuQueryError as err:
assert err.msg.contains( re"""Parameter nope not found.""" )

View file

@ -14,7 +14,7 @@ assert typeOf( p ) is KuzuPreparedStatement
try:
discard p.execute
except KuzuQueryException as err:
assert err.msg.contains( re""".*Error executing prepared statement:.*CREAET""" )
except KuzuQueryError as err:
assert err.msg.contains( re"""Parser exception: extraneous input 'CREAET'""" )

View file

@ -12,6 +12,6 @@ q = conn.query( "MATCH (d:Doop) RETURN d.thing" )
try:
discard q.getNext
except KuzuIndexException as err:
except KuzuIndexError as err:
assert err.msg.contains( re"""Query iteration past end.""" )

View file

@ -15,6 +15,6 @@ let tup = q.getNext
try:
echo tup[22]
except KuzuIndexException as err:
except KuzuIndexError as err:
assert err.msg.contains( re"""Unable to fetch tuple value at idx 22.""" )

View file

@ -0,0 +1,21 @@
# 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, thing STRING, PRIMARY KEY(id) )" )
q = conn.query( "CREATE (d:Doop {thing: 'okay!'})" )
q = conn.query( "MATCH (d:Doop) RETURN d.id, d.thing, d" )
var row = q.getNext
var id = row[0]
var thing = row[1]
var node = row[2]
assert id.kind == KUZU_INT64
assert thing.kind == KUZU_STRING
assert node.kind == KUZU_NODE