Rename the project to nim-ladybug, after kuzu's sudden October abandoning. Picked up by a new party under the new name, lets see where this goes!
No new functionality, just rename and docs updates.
This commit is contained in:
parent
ee0e8a72c0
commit
76718fa49f
61 changed files with 3030 additions and 2998 deletions
|
|
@ -1,4 +1,11 @@
|
|||
# Release History for nim-kuzu
|
||||
# Release History for nim-ladybug
|
||||
|
||||
---
|
||||
## v0.7.0 [2025-11-04] Mahlon E. Smith <mahlon@martini.nu>
|
||||
|
||||
Rename the project from Kuzu to Ladybug, following new upstream efforts to keep
|
||||
the project going after the Kuzu originator sudden abandonment.
|
||||
|
||||
|
||||
---
|
||||
## v0.6.1 [2025-07-19] Mahlon E. Smith <mahlon@martini.nu>
|
||||
|
|
|
|||
30
README.md
30
README.md
|
|
@ -1,18 +1,18 @@
|
|||
|
||||
# Nim Kuzu
|
||||
# Nim Ladybug
|
||||
|
||||
home
|
||||
: https://code.martini.nu/mahlon/nim-kuzu
|
||||
: https://code.martini.nu/mahlon/nim-ladybug
|
||||
|
||||
github_mirror
|
||||
: https://github.com/mahlonsmith/nim-kuzu
|
||||
: https://github.com/mahlonsmith/nim-ladybug
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
This is a Nim binding for the [Kuzu](https://kuzudb.com) graph database library.
|
||||
This is a Nim binding for the [LadybugDB](https://ladybugdb.com) graph database library.
|
||||
|
||||
Kuzu is an embedded graph database built for query speed and scalability. It is
|
||||
Ladybug is an embedded graph database built for query speed and scalability. It is
|
||||
optimized for handling complex join-heavy analytical workloads on very large
|
||||
graphs, with the following core feature set:
|
||||
|
||||
|
|
@ -25,22 +25,21 @@ graphs, with the following core feature set:
|
|||
- Multi-core query parallelism
|
||||
- Serializable ACID transactions
|
||||
|
||||
For more information about Kuzu itself, see its
|
||||
[documentation](https://docs.kuzudb.com/).
|
||||
For more information about Ladybug itself, see its
|
||||
[documentation](https://docs.ladybugdb.com/).
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* A functioning Nim >= 2 installation
|
||||
- [KuzuDB](https://kuzudb.com) to be locally installed!
|
||||
- [LadybugDB](https://ladybugdb.com) to be locally installed!
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
$ nimble install kuzu
|
||||
$ nimble install ladybug
|
||||
|
||||
The current version of this library is built for Kuzu v0.11.3, which sadly
|
||||
seems may have been the final release of kuzudb. :(
|
||||
The current version of this library is built for Ladybug v0.12.0.
|
||||
|
||||
|
||||
## Usage
|
||||
|
|
@ -53,11 +52,11 @@ You can also find a bunch of working examples in the tests.
|
|||
## Contributing
|
||||
|
||||
You can check out the current development source via Git/Jujutsu at its
|
||||
[home repo](https://code.martini.nu/mahlon/nim-kuzu), or the
|
||||
[project mirror](https://github.com/mahlonsmith/nim-kuzu).
|
||||
[home repo](https://code.martini.nu/mahlon/nim-ladybug), or the
|
||||
[project mirror](https://github.com/mahlonsmith/nim-ladybug).
|
||||
|
||||
After checking out the source, uncomment the development dependencies
|
||||
from the `kuzu.nimble` file, and run:
|
||||
from the `ladybug.nimble` file, and run:
|
||||
|
||||
$ nimble setup
|
||||
|
||||
|
|
@ -71,4 +70,5 @@ development.
|
|||
- Mahlon E. Smith <mahlon@martini.nu>
|
||||
|
||||
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.
|
||||
KuzuDB (0.4.x) that I found after starting this project (the predecessor to
|
||||
LadybugDB.)
|
||||
|
|
|
|||
173
USAGE.md
173
USAGE.md
|
|
@ -12,24 +12,28 @@ a lot and it's hard to know where to start.
|
|||
|
||||
## Prior Reading
|
||||
|
||||
If you're just starting with Kuzu or graph databases, it's probably a good idea
|
||||
to familiarize yourself with the [Kuzu Documentation](https://docs.kuzudb.com/)
|
||||
and the [Cypher Language](https://docs.kuzudb.com/tutorials/cypher/). This
|
||||
library won't do much for you by itself without a basic understanding of Kuzu usage.
|
||||
If you're just starting with Ladybug or graph databases, it's probably a good idea
|
||||
to familiarize yourself with the [Ladybug Documentation](https://docs.ladybugdb.com/)
|
||||
and the [Cypher Language](https://docs.ladybugdb.com/tutorials/cypher/). This
|
||||
library won't do much for you by itself without a basic understanding of Ladybug usage.
|
||||
|
||||
|
||||
## Checking Compatibility
|
||||
|
||||
This is a wrapper (with some additional niceties) for the system-installed Kuzu
|
||||
This is a wrapper (with some additional niceties) for the system-installed Ladybug
|
||||
shared library. As such, the version of this library might not match with what
|
||||
you currently have installed.
|
||||
|
||||
Check the [README](README.md), the [History](History.md), and the following
|
||||
table to ensure you're using the correct version for your Kuzu
|
||||
installation. I'll make a modest effort for backwards compatibility while Kuzu
|
||||
table to ensure you're using the correct version for your Ladybug
|
||||
installation. I'll make a modest effort for backwards compatibility while Ladybug
|
||||
is pre 1.0, and in practice, mismatched versions *might* work. Don't count too
|
||||
heavily on it. :-) Once there's a 1.0, this should be less chaotic.
|
||||
|
||||
Ladybug was continued from the KuzuDB project, which was hastily abandoned in
|
||||
October of 2025. Previous versions used a "Kuzu" namespace.
|
||||
|
||||
|
||||
| Kuzu Library Version | Nim Kuzu Minimum Version |
|
||||
| -------------------- | ------------------------ |
|
||||
| v0.8.2 | v0.1.0 |
|
||||
|
|
@ -37,34 +41,49 @@ heavily on it. :-) Once there's a 1.0, this should be less chaotic.
|
|||
| v0.10.0 | v0.4.0 |
|
||||
| v0.11.0 | v0.5.0 |
|
||||
|
||||
You can use the `kuzuVersionCompatible()` function (along with the
|
||||
`kuzuGetVersion()` and the `KUZU_VERSION` constant) to quickly check if things
|
||||
|
||||
| Ladybug Library Version | Nim Ladybug Minimum Version |
|
||||
| ----------------------- | --------------------------- |
|
||||
| v0.12.0 | v0.7.0 |
|
||||
|
||||
|
||||
You can use the `lbugVersionCompatible()` function (along with the
|
||||
`lbugGetVersion()` and the `LBUG_VERSION` constant) to quickly check if things
|
||||
are looking right.
|
||||
|
||||
```nim
|
||||
import kuzu
|
||||
import ladybug
|
||||
|
||||
echo KUZU_VERSION #=> "0.1.0"
|
||||
echo kuzuGetVersion() #=> "0.8.2"
|
||||
echo kuzuVersionCompatible() #=> true
|
||||
echo LBUG_VERSION #=> "0.7.0"
|
||||
echo lbugGetVersion() #=> "0.12.0"
|
||||
echo lbugVersionCompatible() #=> true
|
||||
```
|
||||
|
||||
|
||||
## Namespace
|
||||
|
||||
The LadybugDB project internals are all prefixed with `lbug`. This wrapper
|
||||
follows suit.
|
||||
|
||||
An exception to that is the import. You can import "ladybug" or "lbug", it's
|
||||
functionally equivalent.
|
||||
|
||||
|
||||
## Connecting to a Database
|
||||
|
||||
Just call `newKuzuDatabase()`. Without an argument (or with an empty string),
|
||||
Just call `newLbugDatabase()`. Without an argument (or with an empty string),
|
||||
the database is in-memory. Any other argument is considered a filesystem path
|
||||
-- it will create an empty database if the path is currently non-existent, or
|
||||
open an existing database otherwise.
|
||||
|
||||
```nim
|
||||
# "db" is in-memory and will evaporate when the process ends.
|
||||
var db = newKuzuDatabase()
|
||||
var db = newLbugDatabase()
|
||||
```
|
||||
|
||||
```nim
|
||||
# "db" is persistent, stored in the file "data.kz".
|
||||
var db = newKuzuDatabase("data.kz")
|
||||
var db = newLbugDatabase("data.kz")
|
||||
```
|
||||
The database path is retained, and can be recalled via `db.path`.
|
||||
|
||||
|
|
@ -86,19 +105,19 @@ if db.config.enable_compression:
|
|||
echo "Yes!"
|
||||
```
|
||||
|
||||
You can alter configuration options when connecting by passing a `kuzuConfig`
|
||||
object as the second argument to `newKuzuDatabase()`:
|
||||
You can alter configuration options when connecting by passing a `lbugConfig`
|
||||
object as the second argument to `newLbugDatabase()`:
|
||||
|
||||
```nim
|
||||
# Open a readonly handle.
|
||||
var db = newKuzuDatabase( "data.kz", kuzuConfig( read_only=true ) )
|
||||
var db = newLbugDatabase( "data.kz", lbugConfig( read_only=true ) )
|
||||
```
|
||||
|
||||
### The Connection
|
||||
|
||||
All interaction with the database is performed via a connection object. There
|
||||
are limitations to database handles and connection objects -- see the
|
||||
[Kuzu Concurrency](https://docs.kuzudb.com/concurrency/) docs for details!
|
||||
[Lbug Concurrency](https://docs.ladybugdb.com/concurrency/) docs for details!
|
||||
|
||||
Call `connect` on an open database handle to create a new connection:
|
||||
|
||||
|
|
@ -121,10 +140,10 @@ conn.queryInterrupt()
|
|||
|
||||
You can perform a basic query via the appropriately named `query()` function on
|
||||
the connection. Via this method, queries are run immediately. A
|
||||
`KuzuQueryResult` is returned - this is the object you'll be interacting with to
|
||||
`LbugQueryResult` is returned - this is the object you'll be interacting with to
|
||||
see results.
|
||||
|
||||
A `KuzuQueryResult` can be turned into a string to quickly see the column
|
||||
A `LbugQueryResult` can be turned into a string to quickly see the column
|
||||
headers and all tuple results:
|
||||
|
||||
```nim
|
||||
|
|
@ -154,7 +173,7 @@ echo res.execution_time #=> 1.624
|
|||
assert res.column_names == @["hi", "pin", "list"]
|
||||
|
||||
# Return the column data types as a sequence.
|
||||
assert res.column_types == @[KUZU_STRING, KUZU_INT64, KUZU_LIST]
|
||||
assert res.column_types == @[LBUG_STRING, LBUG_INT64, LBUG_LIST]
|
||||
```
|
||||
|
||||
### Prepared Statements
|
||||
|
|
@ -172,7 +191,7 @@ RETURN
|
|||
[1,2,3] AS list
|
||||
"""
|
||||
|
||||
# This returns a KuzuQueryResult, just like `conn.query()`.
|
||||
# This returns a LbugQueryResult, just like `conn.query()`.
|
||||
var res = stmt.execute()
|
||||
```
|
||||
|
||||
|
|
@ -198,13 +217,13 @@ echo $res #=>
|
|||
#### Type Conversion
|
||||
|
||||
When binding variables to a prepared statement, most Nim types are automatically
|
||||
converted to their respective Kuzu types.
|
||||
converted to their respective Ladybug types.
|
||||
|
||||
```nim
|
||||
var stmt = conn.prepare( """RETURN $num AS num""" )
|
||||
var res = stmt.execute( (num: 12) )
|
||||
|
||||
assert res.column_types[0] == KUZU_INT32
|
||||
assert res.column_types[0] == LBUG_INT32
|
||||
```
|
||||
|
||||
This might not necessarily be what you want - sometimes you'd rather be strict
|
||||
|
|
@ -215,37 +234,37 @@ You can use [integer type suffixes](https://nim-lang.org/docs/manual.html#lexica
|
|||
|
||||
```nim
|
||||
var stmt = conn.prepare( """RETURN $num AS num""" )
|
||||
var res: KuzuQueryResult
|
||||
var res: LbugQueryResult
|
||||
|
||||
res = stmt.execute( (num: 12'u64) )
|
||||
assert res.column_types[0] == KUZU_UINT64
|
||||
assert res.column_types[0] == LBUG_UINT64
|
||||
|
||||
res = stmt.execute( (num: 12.float) )
|
||||
assert res.column_types[0] == KUZU_DOUBLE
|
||||
assert res.column_types[0] == LBUG_DOUBLE
|
||||
```
|
||||
|
||||
#### Kuzu Specific Types
|
||||
#### Ladybug Specific Types
|
||||
|
||||
In the example above, you may have noticed the `LIST_CREATION($list)` in the
|
||||
prepared query, and that we passed a string `1,2,3` as the `$list` parameter.
|
||||
|
||||
This is a useful way to easily use most Kuzu types without needing corresponding
|
||||
This is a useful way to easily use most Ladybug types without needing corresponding
|
||||
Nim ones -- if you're inserting into a table that is using a custom type, you
|
||||
can cast it using the query itself during insertion!
|
||||
|
||||
This has the additional advantage of letting Kuzu error check the validity of
|
||||
This has the additional advantage of letting Ladybug error check the validity of
|
||||
the content, and it works with the majority of types.
|
||||
|
||||
An extended example:
|
||||
|
||||
```nim
|
||||
import std/sequtils
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
var db = newKuzuDatabase()
|
||||
var db = newLbugDatabase()
|
||||
var conn = db.connect
|
||||
|
||||
var res: KuzuQueryResult
|
||||
var res: LbugQueryResult
|
||||
|
||||
# Create a node table.
|
||||
#
|
||||
|
|
@ -277,7 +296,7 @@ CREATE (e:Example {
|
|||
})
|
||||
"""
|
||||
|
||||
# Add a node row that contains specific Kuzu types.
|
||||
# Add a node row that contains specific Ladybug types.
|
||||
#
|
||||
res = stmt.execute((
|
||||
num: 2,
|
||||
|
|
@ -295,26 +314,26 @@ echo $res #=>
|
|||
# e.id|e.num|e.done|e.comment|e.karma|e.thing|e.created|e.activity
|
||||
# 0|2|True|Types!|16.700000|e0e7232e-bec9-4625-9822-9d1a31ea6f93|2025-03-29|2025-03-29 00:00:00
|
||||
|
||||
# Show column names and their Kuzu types.
|
||||
# Show column names and their Ladybug types.
|
||||
for pair in res.column_names.zip( res.column_types ):
|
||||
echo pair #=>
|
||||
# ("e.id", KUZU_SERIAL)
|
||||
# ("e.num", KUZU_UINT8)
|
||||
# ("e.done", KUZU_BOOL)
|
||||
# ("e.comment", KUZU_STRING)
|
||||
# ("e.karma", KUZU_DOUBLE)
|
||||
# ("e.thing", KUZU_UUID)
|
||||
# ("e.created", KUZU_DATE)
|
||||
# ("e.activity", KUZU_TIMESTAMP)
|
||||
# ("e.id", LBUG_SERIAL)
|
||||
# ("e.num", LBUG_UINT8)
|
||||
# ("e.done", LBUG_BOOL)
|
||||
# ("e.comment", LBUG_STRING)
|
||||
# ("e.karma", LBUG_DOUBLE)
|
||||
# ("e.thing", LBUG_UUID)
|
||||
# ("e.created", LBUG_DATE)
|
||||
# ("e.activity", LBUG_TIMESTAMP)
|
||||
```
|
||||
|
||||
## Reading Results
|
||||
|
||||
So far we've just been showing values by converting the entire `KuzuQueryResult`
|
||||
So far we've just been showing values by converting the entire `LbugQueryResult`
|
||||
to a string. Convenient for quick examples and debugging, but not much else.
|
||||
|
||||
A `KuzuQueryResult` is an iterator. You can use regular Nim functions that yield
|
||||
each `KuzuFlatTuple` -- essentially, each row that was returned in a result set.
|
||||
A `LbugQueryResult` is an iterator. You can use regular Nim functions that yield
|
||||
each `LbugFlatTuple` -- essentially, each row that was returned in a result set.
|
||||
|
||||
```nim
|
||||
var res = conn.query """
|
||||
|
|
@ -323,7 +342,7 @@ var res = conn.query """
|
|||
RETURN items, thing
|
||||
"""
|
||||
|
||||
# KuzuFlatTuple can be stringified just like the result set.
|
||||
# LbugFlatTuple can be stringified just like the result set.
|
||||
for row in res:
|
||||
echo row #=>
|
||||
# 1|thing
|
||||
|
|
@ -333,7 +352,7 @@ for row in res:
|
|||
|
||||
Once iteration has reached the end, it is automatically rewound for reuse.
|
||||
|
||||
You can manually get the next `KuzuFlatTuple` via `getNext()`. Calling
|
||||
You can manually get the next `LbugFlatTuple` via `getNext()`. Calling
|
||||
`getNext()` after the last row results in an error. Use `hasNext()` to check
|
||||
before calling.
|
||||
|
||||
|
|
@ -350,23 +369,23 @@ if res.hasNext:
|
|||
|
||||
echo res.getNext #=> 2
|
||||
echo res.getNext #=> 3
|
||||
echo res.getNext #=> KuzuIterationError exception!
|
||||
echo res.getNext #=> LbugIterationError exception!
|
||||
```
|
||||
|
||||
Manually rewind the `KuzuQueryResult` via `rewind()`.
|
||||
Manually rewind the `LbugQueryResult` via `rewind()`.
|
||||
|
||||
|
||||
## Multiple Query Results
|
||||
|
||||
A query can potentially return any number of separate statements. In the case
|
||||
of more potential `RETURN`s, the query will only contain the first. Iterate
|
||||
over linked `KuzuQueryResult` objects with the `sets()` iterator to retreive the
|
||||
over linked `LbugQueryResult` objects with the `sets()` iterator to retreive the
|
||||
remaining:
|
||||
|
||||
```nim
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
let query = conn.query """
|
||||
|
|
@ -400,8 +419,8 @@ for set in query.sets:
|
|||
|
||||
## Working with Values
|
||||
|
||||
A `KuzuFlatTuple` contains the entire row. You can index a value at its column
|
||||
position, returning a `KuzuValue`.
|
||||
A `LbugFlatTuple` contains the entire row. You can index a value at its column
|
||||
position, returning a `LbugValue`.
|
||||
|
||||
```nim
|
||||
var res = conn.query """
|
||||
|
|
@ -419,27 +438,27 @@ var row = res.getNext
|
|||
for idx in ( 0 .. res.num_columns-1 ):
|
||||
var value = row[idx]
|
||||
echo res.column_names[idx], ": ", value, " (", value.kind, ")" #=>
|
||||
# num: 1 (KUZU_INT64)
|
||||
# done: True (KUZU_BOOL)
|
||||
# comment: A comment (KUZU_STRING)
|
||||
# karma: 12.840000 (KUZU_DOUBLE)
|
||||
# thing: b41deae0-dddf-430b-981d-3fb93823e495 (KUZU_UUID)
|
||||
# created: 2025-03-29 (KUZU_DATE)
|
||||
# num: 1 (LBUG_INT64)
|
||||
# done: True (LBUG_BOOL)
|
||||
# comment: A comment (LBUG_STRING)
|
||||
# karma: 12.840000 (LBUG_DOUBLE)
|
||||
# thing: b41deae0-dddf-430b-981d-3fb93823e495 (LBUG_UUID)
|
||||
# created: 2025-03-29 (LBUG_DATE)
|
||||
```
|
||||
|
||||
### Types
|
||||
|
||||
A `KuzuValue` can always be stringified, irrespective of its Kuzu type. You can
|
||||
A `LbugValue` can always be stringified, irrespective of its Lbug type. You can
|
||||
check what type it is via the 'kind' property.
|
||||
|
||||
```nim
|
||||
var res = conn.query """RETURN "hello""""
|
||||
var value = res.getNext[0]
|
||||
|
||||
assert value.kind == KUZU_STRING
|
||||
assert value.kind == LBUG_STRING
|
||||
```
|
||||
|
||||
A `KuzuValue` has conversion methods for Nim base types. You'll likely want to
|
||||
A `LbugValue` has conversion methods for Nim base types. You'll likely want to
|
||||
convert it for regular Nim usage:
|
||||
|
||||
```nim
|
||||
|
|
@ -455,12 +474,12 @@ assert value.toInt64 + 1 == 2561
|
|||
|
||||
### Lists
|
||||
|
||||
A `KuzuValue` of type `KUZU_LIST` can be converted to a Nim sequence of
|
||||
`KuzuValues` with the `toList()` function:
|
||||
A `LbugValue` of type `LBUG_LIST` can be converted to a Nim sequence of
|
||||
`LbugValues` with the `toList()` function:
|
||||
|
||||
```nim
|
||||
import std/sequtils
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
var res = conn.query """
|
||||
RETURN [10, 20, 30]
|
||||
|
|
@ -471,16 +490,16 @@ var value = res.getNext[0]
|
|||
var list = value.toList
|
||||
echo list #=> @[10,20,30]
|
||||
|
||||
echo list.map( func(v:KuzuValue): int = v.toInt64 * 10 ) #=> @[100,200,300]
|
||||
echo list.map( func(v:LbugValue): int = v.toInt64 * 10 ) #=> @[100,200,300]
|
||||
```
|
||||
|
||||
|
||||
### Struct-like Objects
|
||||
|
||||
Various Kuzu types can act like a struct - this includes `KUZU_NODE`,
|
||||
`KUZU_REL`, and of course an explicit `KUZU_STRUCT` itself, among others.
|
||||
Various Ladybug types can act like a struct - this includes `LBUG_NODE`,
|
||||
`LBUG_REL`, and of course an explicit `LBUG_STRUCT` itself, among others.
|
||||
|
||||
Convert a `KuzuValue` to a `KuzuStructValue` with `toStruct()`. For
|
||||
Convert a `LbugValue` to a `LbugStructValue` with `toStruct()`. For
|
||||
convenience, this is also aliased to `toNode()` and `toRel()`.
|
||||
|
||||
Once converted, you can access struct values by passing the key name to `[]`:
|
||||
|
|
@ -503,9 +522,9 @@ Here's a more elaborate example, following a node path:
|
|||
import
|
||||
std/sequtils,
|
||||
std/strformat
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
var db = newKuzuDatabase()
|
||||
var db = newLbugDatabase()
|
||||
var conn = db.connect
|
||||
|
||||
var res = conn.query """
|
||||
|
|
@ -540,7 +559,7 @@ res = conn.query """
|
|||
#
|
||||
for row in res:
|
||||
var since = row[0]
|
||||
var people = row[1].toList.map( proc(p:KuzuValue):KuzuStructValue = p.toNode )
|
||||
var people = row[1].toList.map( proc(p:LbugValue):LbugStructValue = p.toNode )
|
||||
echo &"""{people[0]["name"]} has known {people[1]["name"]} since {since}.""" #=>
|
||||
# Bob has known Bruce since 2003.
|
||||
# Bob has known Alice since 2009.
|
||||
|
|
@ -550,7 +569,7 @@ for row in res:
|
|||
|
||||
### Blobs
|
||||
|
||||
Kuzu can store small chunks of opaque binary data. For these BLOB columns,
|
||||
Ladybug can store small chunks of opaque binary data. For these BLOB columns,
|
||||
using `toBlob` will return the raw sequence of bytes.
|
||||
|
||||
```nim
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
version = "0.6.1"
|
||||
version = "0.7.0"
|
||||
author = "Mahlon E. Smith"
|
||||
description = "Kuzu is an embedded graph database built for query speed and scalability."
|
||||
description = "Ladybug is an embedded graph database built for query speed and scalability."
|
||||
license = "BSD-3-Clause"
|
||||
srcDir = "src"
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ requires "nim ^= 2.0.0"
|
|||
#requires "zip ^= 0.3.1"
|
||||
|
||||
task makewrapper, "Generate the C wrapper using Futhark":
|
||||
exec "nim c -d:futharkWrap --outdir=tmp/ src/kuzu.nim"
|
||||
exec "nim c -d:futharkWrap --outdir=tmp/ src/lbug.nim"
|
||||
|
||||
task test, "Run the test suite.":
|
||||
exec "testament --megatest:off all"
|
||||
|
|
@ -24,5 +24,5 @@ task docs, "Generate automated documentation.":
|
|||
exec "nim md2html --project --outdir:docs README.md"
|
||||
exec "nim md2html --project --outdir:docs History.md"
|
||||
exec "nim md2html --project --outdir:docs USAGE.md"
|
||||
exec "nim doc --project --outdir:docs src/kuzu.nim"
|
||||
exec "nim doc --project --outdir:docs src/ladybug.nim"
|
||||
|
||||
49
src/kuzu.nim
49
src/kuzu.nim
|
|
@ -1,49 +0,0 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
#
|
||||
|
||||
{.passL:"-lkuzu".}
|
||||
|
||||
when defined( futharkWrap ):
|
||||
import futhark, os
|
||||
|
||||
importc:
|
||||
outputPath currentSourcePath.parentDir / "kuzu" / "0.11.3.nim"
|
||||
"kuzu.h"
|
||||
else:
|
||||
include "kuzu/0.11.3.nim"
|
||||
|
||||
import
|
||||
std/files,
|
||||
std/paths,
|
||||
std/strformat,
|
||||
std/strutils
|
||||
|
||||
# Order very much matters here pre Nim 3.0 multi-pass compiling.
|
||||
include
|
||||
"kuzu/constants.nim",
|
||||
"kuzu/types.nim",
|
||||
"kuzu/config.nim",
|
||||
"kuzu/database.nim",
|
||||
"kuzu/connection.nim",
|
||||
"kuzu/value.nim",
|
||||
"kuzu/tuple.nim",
|
||||
"kuzu/queries.nim"
|
||||
|
||||
|
||||
proc kuzuVersionCompatible*(): bool =
|
||||
## Returns true if the system installed Kuzu library
|
||||
## is the expected version of this library wrapper.
|
||||
result = KUZU_EXPECTED_LIBVERSION == $kuzuGetVersion()
|
||||
|
||||
|
||||
when isMainModule:
|
||||
echo "Nim-Kuzu version: ", KUZU_VERSION,
|
||||
". Expected library version: ", KUZU_EXPECTED_LIBVERSION, "."
|
||||
echo "Installed Kuzu library version ", kuzuGetVersion(),
|
||||
" (storage version ", kuzuGetStorageVersion(), ")"
|
||||
if kuzuVersionCompatible():
|
||||
echo "Versions match!"
|
||||
else:
|
||||
echo "This library wraps a different version of Kuzu than what is installed."
|
||||
echo "Behavior may be unexpected!"
|
||||
|
||||
2168
src/kuzu/0.11.3.nim
2168
src/kuzu/0.11.3.nim
File diff suppressed because it is too large
Load diff
|
|
@ -1,24 +0,0 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
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,
|
||||
read_only = KUZU_DEFAULT_CONFIG.read_only,
|
||||
max_db_size = KUZU_DEFAULT_CONFIG.max_db_size,
|
||||
auto_checkpoint = KUZU_DEFAULT_CONFIG.auto_checkpoint,
|
||||
checkpoint_threshold = KUZU_DEFAULT_CONFIG.checkpoint_threshold
|
||||
): kuzu_system_config =
|
||||
## Returns a new kuzu database configuration object.
|
||||
|
||||
return kuzu_system_config(
|
||||
buffer_pool_size: buffer_pool_size,
|
||||
max_num_threads: max_num_threads,
|
||||
enable_compression: enable_compression,
|
||||
read_only: read_only,
|
||||
max_db_size: max_db_size,
|
||||
auto_checkpoint: auto_checkpoint,
|
||||
checkpoint_threshold: checkpoint_threshold
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
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 )
|
||||
|
||||
|
||||
func connect*( db: KuzuDatabase ): KuzuConnection =
|
||||
## Connect to a database.
|
||||
result = new KuzuConnection
|
||||
if kuzu_connection_init( addr db.handle, addr result.handle ) == KuzuSuccess:
|
||||
result.valid = true
|
||||
else:
|
||||
raise newException( KuzuException, "Unable to connect to the database." )
|
||||
|
||||
|
||||
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 )
|
||||
|
||||
|
||||
func queryInterrupt*( conn: KuzuConnection ) =
|
||||
## Cancel any running queries.
|
||||
kuzu_connection_interrupt( addr conn.handle )
|
||||
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
const KUZU_VERSION* = "0.6.2"
|
||||
const KUZU_EXPECTED_LIBVERSION* = "0.11.3"
|
||||
const BLOB_MAXSIZE = 4096
|
||||
|
||||
let KUZU_DEFAULT_CONFIG* = kuzu_default_system_config()
|
||||
|
||||
|
|
@ -1,197 +0,0 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
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 )
|
||||
|
||||
|
||||
# Forward declarations.
|
||||
func hasNextSet( query: KuzuQueryResult ): bool
|
||||
func getNextSet( query: KuzuQueryResult ): KuzuQueryResult
|
||||
|
||||
|
||||
func getQueryMetadata( query: KuzuQueryResult, getAllQueryResults=false ) =
|
||||
## 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 )
|
||||
|
||||
# Summary information.
|
||||
var summary: kuzu_query_summary
|
||||
discard kuzu_query_result_get_query_summary( addr query.handle, addr summary )
|
||||
query.compile_time = kuzu_query_summary_get_compiling_time( addr summary )
|
||||
query.execution_time = kuzu_query_summary_get_execution_time( addr summary )
|
||||
kuzu_query_summary_destroy( addr summary )
|
||||
|
||||
# Pull any additional query results.
|
||||
query.sets = @[]
|
||||
if getAllQueryResults:
|
||||
while query.hasNextSet:
|
||||
query.sets.add( query.getNextSet )
|
||||
|
||||
# Column information.
|
||||
query.column_types = @[]
|
||||
query.column_names = @[]
|
||||
if query.num_columns == 0: return
|
||||
for idx in ( 0 .. query.num_columns-1 ):
|
||||
|
||||
# types
|
||||
#
|
||||
var logical_type: kuzu_logical_type
|
||||
discard kuzu_query_result_get_column_data_type(
|
||||
addr query.handle,
|
||||
idx,
|
||||
addr logical_type
|
||||
)
|
||||
query.column_types.add( kuzu_data_type_get_id( addr logical_type ))
|
||||
kuzu_data_type_destroy( addr logical_type )
|
||||
|
||||
# names
|
||||
#
|
||||
var name: cstring
|
||||
discard kuzu_query_result_get_column_name(
|
||||
addr query.handle,
|
||||
idx,
|
||||
addr name
|
||||
)
|
||||
query.column_names.add( $name )
|
||||
kuzu_destroy_string( name )
|
||||
|
||||
|
||||
func hasNextSet( query: KuzuQueryResult ): bool =
|
||||
## Returns +true+ if there are more result sets to be consumed.
|
||||
result = kuzu_query_result_has_next_query_result( addr query.handle )
|
||||
|
||||
|
||||
func getNextSet( query: KuzuQueryResult ): KuzuQueryResult =
|
||||
## Consume and return the next query set result, or raise a KuzuIterationError
|
||||
## if at the end of sets.
|
||||
result = new KuzuQueryResult
|
||||
if kuzu_query_result_get_next_query_result( addr query.handle, addr result.handle ) == KuzuSuccess:
|
||||
result.valid = true
|
||||
result.getQueryMetadata()
|
||||
else:
|
||||
raise newException( KuzuIterationError, &"Query iteration past end of set." )
|
||||
|
||||
|
||||
func query*( conn: KuzuConnection, query: string ): KuzuQueryResult =
|
||||
## Perform a database +query+ and return the result.
|
||||
result = new KuzuQueryResult
|
||||
|
||||
if kuzu_connection_query( addr conn.handle, query, addr result.handle ) == KuzuSuccess:
|
||||
result.valid = true
|
||||
result.getQueryMetadata( getAllQueryResults=true )
|
||||
else:
|
||||
var err = kuzu_query_result_get_error_message( addr result.handle )
|
||||
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 )
|
||||
|
||||
|
||||
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
|
||||
if kuzu_connection_prepare( addr conn.handle, query, addr result.handle ) == KuzuSuccess:
|
||||
result.conn = conn
|
||||
result.valid = true
|
||||
else:
|
||||
var err = kuzu_prepared_statement_get_error_message( addr result.handle )
|
||||
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*(
|
||||
prepared: KuzuPreparedStatement,
|
||||
params: tuple = ()
|
||||
): KuzuQueryResult =
|
||||
## Bind variables in *params* to the statement, and return
|
||||
## a KuzuQueryResult.
|
||||
|
||||
result = new KuzuQueryResult
|
||||
|
||||
for key, val in params.fieldPairs:
|
||||
prepared.handle.bindValue( key, val )
|
||||
|
||||
if kuzu_connection_execute(
|
||||
addr prepared.conn.handle,
|
||||
addr prepared.handle,
|
||||
addr result.handle
|
||||
) == KuzuSuccess:
|
||||
result.valid = false
|
||||
result.getQueryMetadata()
|
||||
else:
|
||||
var err = kuzu_query_result_get_error_message( addr result.handle )
|
||||
raise newException( KuzuQueryError, &"Error executing prepared statement: {err}" )
|
||||
|
||||
|
||||
func `$`*( query: KuzuQueryResult ): string =
|
||||
## Return the entire result set as a string.
|
||||
result = $kuzu_query_result_to_string( addr query.handle )
|
||||
|
||||
|
||||
func hasNext*( query: KuzuQueryResult ): bool =
|
||||
## Returns +true+ if there are more tuples to be consumed.
|
||||
result = kuzu_query_result_has_next( addr query.handle )
|
||||
|
||||
|
||||
func getNext*( query: KuzuQueryResult ): KuzuFlatTuple =
|
||||
## Consume and return the next tuple result, or raise a KuzuIterationError
|
||||
## if at the end of the result tuples.
|
||||
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( KuzuIterationError, &"Query iteration past end of tuples." )
|
||||
|
||||
|
||||
func rewind*( query: KuzuQueryResult ) =
|
||||
## Reset query iteration back to the beginning.
|
||||
kuzu_query_result_reset_iterator( addr query.handle )
|
||||
|
||||
|
||||
iterator items*( query: KuzuQueryResult ): KuzuFlatTuple =
|
||||
## Iterate available tuples, yielding to the block.
|
||||
while query.hasNext:
|
||||
yield query.getNext
|
||||
query.rewind
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
# NOTE: Constructor in queries.nim, #getNext
|
||||
|
||||
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 )
|
||||
|
||||
|
||||
func `$`*( tpl: KuzuFlatTuple ): string =
|
||||
## Stringify a tuple.
|
||||
result = $kuzu_flat_tuple_to_string( addr tpl.handle )
|
||||
result.removeSuffix( "\n" )
|
||||
|
||||
|
||||
func `[]`*( tpl: KuzuFlatTuple, idx: int|uint64 ): 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( KuzuIndexError,
|
||||
&"Unable to fetch tuple value at idx {idx}. ({tpl.num_columns} column(s).)" )
|
||||
|
||||
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
type
|
||||
KuzuDBType* = enum
|
||||
disk, memory
|
||||
|
||||
KuzuDatabaseObj = object
|
||||
handle: kuzu_database
|
||||
path*: string
|
||||
kind*: KuzuDBType
|
||||
config*: kuzu_system_config
|
||||
valid = false
|
||||
KuzuDatabase* = ref KuzuDatabaseObj
|
||||
|
||||
KuzuConnectionObj = object
|
||||
handle: kuzu_connection
|
||||
valid = false
|
||||
KuzuConnection* = ref KuzuConnectionObj
|
||||
|
||||
KuzuQueryResultObj = object
|
||||
handle: kuzu_query_result
|
||||
num_columns*: uint64 = 0
|
||||
num_tuples*: uint64 = 0
|
||||
compile_time*: cdouble = 0
|
||||
execution_time*: cdouble = 0
|
||||
column_types*: seq[ kuzu_data_type_id ]
|
||||
column_names*: seq[ string ]
|
||||
sets*: seq[ KuzuQueryResult ]
|
||||
valid = false
|
||||
KuzuQueryResult* = ref KuzuQueryResultObj
|
||||
|
||||
KuzuPreparedStatementObj = object
|
||||
handle: kuzu_prepared_statement
|
||||
conn: KuzuConnection
|
||||
valid = false
|
||||
KuzuPreparedStatement* = ref KuzuPreparedStatementObj
|
||||
|
||||
KuzuFlatTupleObj = object
|
||||
handle: kuzu_flat_tuple
|
||||
num_columns: uint64 = 0
|
||||
valid = false
|
||||
KuzuFlatTuple* = ref KuzuFlatTupleObj
|
||||
|
||||
KuzuValueObj = object
|
||||
handle: kuzu_value
|
||||
valid = false
|
||||
kind*: kuzu_data_type_id
|
||||
KuzuValue* = ref KuzuValueObj
|
||||
|
||||
KuzuStructValueObj = object
|
||||
value: KuzuValue
|
||||
len*: uint64
|
||||
keys*: seq[ string ]
|
||||
KuzuStructValue* = ref KuzuStructValueObj
|
||||
|
||||
KuzuException* = object of CatchableError
|
||||
KuzuQueryError* = object of KuzuException
|
||||
KuzuIndexError* = object of KuzuException
|
||||
KuzuIterationError* = object of KuzuException
|
||||
KuzuTypeError* = object of KuzuException
|
||||
|
||||
|
|
@ -1,197 +0,0 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
# NOTE: Constructor in tuples.nim, #[]
|
||||
|
||||
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 )
|
||||
|
||||
|
||||
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 )
|
||||
value.kind = kuzu_data_type_get_id( addr logical_type )
|
||||
kuzu_data_type_destroy( addr logical_type )
|
||||
|
||||
|
||||
template checkType( kind: kuzu_data_type_id, valid_types: set ) =
|
||||
## Raises a KuzuTypeError if the type conversion is incompatible.
|
||||
if kind notin valid_types:
|
||||
let msg = "Mismatched types: " & $kind & " != " & $valid_types
|
||||
raise newException( KuzuTypeError, msg )
|
||||
|
||||
|
||||
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.
|
||||
checkType( value.kind, {KUZU_BOOL} )
|
||||
assert( kuzu_value_get_bool( addr value.handle, addr result ) == KuzuSuccess )
|
||||
|
||||
|
||||
func toInt8*( value: KuzuValue ): int8 =
|
||||
## Conversion from Kuzu type to Nim.
|
||||
checkType( value.kind, {KUZU_INT8} )
|
||||
assert( kuzu_value_get_int8( addr value.handle, addr result ) == KuzuSuccess )
|
||||
|
||||
|
||||
func toInt16*( value: KuzuValue ): int16 =
|
||||
## Conversion from Kuzu type to Nim.
|
||||
checkType( value.kind, {KUZU_INT16} )
|
||||
assert( kuzu_value_get_int16( addr value.handle, addr result ) == KuzuSuccess )
|
||||
|
||||
|
||||
func toInt32*( value: KuzuValue ): int32 =
|
||||
## Conversion from Kuzu type to Nim.
|
||||
checkType( value.kind, {KUZU_INT32} )
|
||||
assert( kuzu_value_get_int32( addr value.handle, addr result ) == KuzuSuccess )
|
||||
|
||||
|
||||
func toInt64*( value: KuzuValue ): int64 =
|
||||
## Conversion from Kuzu type to Nim.
|
||||
checkType( value.kind, {KUZU_INT64} )
|
||||
assert( kuzu_value_get_int64( addr value.handle, addr result ) == KuzuSuccess )
|
||||
|
||||
|
||||
func toUint8*( value: KuzuValue ): uint8 =
|
||||
## Conversion from Kuzu type to Nim.
|
||||
checkType( value.kind, {KUZU_UINT8} )
|
||||
assert( kuzu_value_get_uint8( addr value.handle, addr result ) == KuzuSuccess )
|
||||
|
||||
|
||||
func toUint16*( value: KuzuValue ): uint16 =
|
||||
## Conversion from Kuzu type to Nim.
|
||||
checkType( value.kind, {KUZU_UINT16} )
|
||||
assert( kuzu_value_get_uint16( addr value.handle, addr result ) == KuzuSuccess )
|
||||
|
||||
|
||||
func toUint32*( value: KuzuValue ): uint32 =
|
||||
## Conversion from Kuzu type to Nim.
|
||||
checkType( value.kind, {KUZU_UINT32} )
|
||||
assert( kuzu_value_get_uint32( addr value.handle, addr result ) == KuzuSuccess )
|
||||
|
||||
|
||||
func toUint64*( value: KuzuValue ): uint64 =
|
||||
## Conversion from Kuzu type to Nim.
|
||||
checkType( value.kind, {KUZU_UINT64} )
|
||||
assert( kuzu_value_get_uint64( addr value.handle, addr result ) == KuzuSuccess )
|
||||
|
||||
|
||||
func toDouble*( value: KuzuValue ): float =
|
||||
## Conversion from Kuzu type to Nim.
|
||||
checkType( value.kind, {KUZU_DOUBLE} )
|
||||
assert( kuzu_value_get_double( addr value.handle, addr result ) == KuzuSuccess )
|
||||
|
||||
|
||||
func toFloat*( value: KuzuValue ): float =
|
||||
## Conversion from Kuzu type to Nim.
|
||||
checkType( value.kind, {KUZU_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.
|
||||
checkType( value.kind, {KUZU_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.
|
||||
checkType( value.kind, {KUZU_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 toBlob*( value: KuzuValue ): seq[ byte ] =
|
||||
## Conversion from Kuzu type to Nim - returns a BLOB as a sequence of bytes.
|
||||
checkType( value.kind, {KUZU_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.
|
||||
checkType( value.kind, {
|
||||
KUZU_STRUCT,
|
||||
KUZU_NODE,
|
||||
KUZU_REL,
|
||||
KUZU_RECURSIVE_REL,
|
||||
KUZU_UNION
|
||||
})
|
||||
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 )
|
||||
|
||||
|
||||
6
src/ladybug.nim
Normal file
6
src/ladybug.nim
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
#
|
||||
# Friendly alias to lbug.
|
||||
|
||||
include "lbug.nim"
|
||||
|
||||
49
src/lbug.nim
Normal file
49
src/lbug.nim
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
#
|
||||
|
||||
{.passL:"-llbug".}
|
||||
|
||||
when defined( futharkWrap ):
|
||||
import futhark, os
|
||||
|
||||
importc:
|
||||
outputPath currentSourcePath.parentDir / "lbug" / "0.12.0.nim"
|
||||
"lbug.h"
|
||||
else:
|
||||
include "lbug/0.12.0.nim"
|
||||
|
||||
import
|
||||
std/files,
|
||||
std/paths,
|
||||
std/strformat,
|
||||
std/strutils
|
||||
|
||||
# Order very much matters here pre Nim 3.0 multi-pass compiling.
|
||||
include
|
||||
"lbug/constants.nim",
|
||||
"lbug/types.nim",
|
||||
"lbug/config.nim",
|
||||
"lbug/database.nim",
|
||||
"lbug/connection.nim",
|
||||
"lbug/value.nim",
|
||||
"lbug/tuple.nim",
|
||||
"lbug/queries.nim"
|
||||
|
||||
|
||||
proc lbugVersionCompatible*(): bool =
|
||||
## Returns true if the system installed LadybugDB library
|
||||
## is the expected version of this library wrapper.
|
||||
result = LBUG_EXPECTED_LIBVERSION == $lbugGetVersion()
|
||||
|
||||
|
||||
when isMainModule:
|
||||
echo "Nim-Ladybug version: ", LBUG_VERSION,
|
||||
". Expected library version: ", LBUG_EXPECTED_LIBVERSION, "."
|
||||
echo "Installed lbug library version ", lbugGetVersion(),
|
||||
" (storage version ", lbugGetStorageVersion(), ")"
|
||||
if lbugVersionCompatible():
|
||||
echo "Versions match!"
|
||||
else:
|
||||
echo "This library wraps a different version of LadybugDB than what is installed."
|
||||
echo "Behavior may be unexpected!"
|
||||
|
||||
2168
src/lbug/0.12.0.nim
Normal file
2168
src/lbug/0.12.0.nim
Normal file
File diff suppressed because it is too large
Load diff
24
src/lbug/config.nim
Normal file
24
src/lbug/config.nim
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
func lbugConfig*(
|
||||
buffer_pool_size = LBUG_DEFAULT_CONFIG.buffer_pool_size,
|
||||
max_num_threads = LBUG_DEFAULT_CONFIG.max_num_threads,
|
||||
enable_compression = LBUG_DEFAULT_CONFIG.enable_compression,
|
||||
read_only = LBUG_DEFAULT_CONFIG.read_only,
|
||||
max_db_size = LBUG_DEFAULT_CONFIG.max_db_size,
|
||||
auto_checkpoint = LBUG_DEFAULT_CONFIG.auto_checkpoint,
|
||||
checkpoint_threshold = LBUG_DEFAULT_CONFIG.checkpoint_threshold
|
||||
): lbug_system_config =
|
||||
## Returns a new lbug database configuration object.
|
||||
|
||||
return lbug_system_config(
|
||||
buffer_pool_size: buffer_pool_size,
|
||||
max_num_threads: max_num_threads,
|
||||
enable_compression: enable_compression,
|
||||
read_only: read_only,
|
||||
max_db_size: max_db_size,
|
||||
auto_checkpoint: auto_checkpoint,
|
||||
checkpoint_threshold: checkpoint_threshold
|
||||
)
|
||||
|
||||
|
||||
27
src/lbug/connection.nim
Normal file
27
src/lbug/connection.nim
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
proc `=destroy`*( conn: LbugConnectionObj ) =
|
||||
## Graceful cleanup for open connection handles.
|
||||
if conn.valid:
|
||||
when defined( debug ): echo &"Destroying connection: {conn}"
|
||||
lbug_connection_destroy( addr conn.handle )
|
||||
|
||||
|
||||
func connect*( db: LbugDatabase ): LbugConnection =
|
||||
## Connect to a database.
|
||||
result = new LbugConnection
|
||||
if lbug_connection_init( addr db.handle, addr result.handle ) == LbugSuccess:
|
||||
result.valid = true
|
||||
else:
|
||||
raise newException( LbugException, "Unable to connect to the database." )
|
||||
|
||||
|
||||
func queryTimeout*( conn: LbugConnection, timeout: uint64 ) =
|
||||
## Set a maximum time limit (in milliseconds) for query runtime.
|
||||
discard lbug_connection_set_query_timeout( addr conn.handle, timeout )
|
||||
|
||||
|
||||
func queryInterrupt*( conn: LbugConnection ) =
|
||||
## Cancel any running queries.
|
||||
lbug_connection_interrupt( addr conn.handle )
|
||||
|
||||
8
src/lbug/constants.nim
Normal file
8
src/lbug/constants.nim
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
const LBUG_VERSION* = "0.7.0"
|
||||
const LBUG_EXPECTED_LIBVERSION* = "0.12.0"
|
||||
const BLOB_MAXSIZE = 4096
|
||||
|
||||
let LBUG_DEFAULT_CONFIG* = lbug_default_system_config()
|
||||
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
proc `=destroy`*( db: KuzuDatabaseObj ) =
|
||||
proc `=destroy`*( db: LbugDatabaseObj ) =
|
||||
## 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 )
|
||||
lbug_database_destroy( addr db.handle )
|
||||
|
||||
|
||||
proc validateDatabase( db: KuzuDatabase ): void =
|
||||
proc validateDatabase( db: LbugDatabase ): void =
|
||||
## Perform basic validity checks against an existing on disk database
|
||||
## for better error messaging.
|
||||
|
||||
|
|
@ -21,20 +21,20 @@ proc validateDatabase( db: KuzuDatabase ): void =
|
|||
let magic = buf[0..3].join
|
||||
let storage_version = buf[4].uint
|
||||
|
||||
if magic != "KUZU":
|
||||
raise newException( KuzuException, "Unable to open database: " &
|
||||
&""""{db.path}" Doesn't appear to be a Kuzu file.""" )
|
||||
if magic != "LBUG":
|
||||
raise newException( LbugException, "Unable to open database: " &
|
||||
&""""{db.path}" Doesn't appear to be a LadybugDB file.""" )
|
||||
|
||||
if storageVersion != kuzuGetStorageVersion():
|
||||
raise newException( KuzuException, "Unable to open database: " &
|
||||
&" mismatched storage versions - file is {storageVersion}, expected {kuzuGetStorageVersion()}." )
|
||||
if storageVersion != lbugGetStorageVersion():
|
||||
raise newException( LbugException, "Unable to open database: " &
|
||||
&" mismatched storage versions - file is {storageVersion}, expected {lbugGetStorageVersion()}." )
|
||||
|
||||
|
||||
proc newKuzuDatabase*( path="", config=kuzuConfig() ): KuzuDatabase =
|
||||
## Create a new Kuzu database handle. Creates an in-memory
|
||||
proc newlbugDatabase*( path="", config=lbugConfig() ): LbugDatabase =
|
||||
## Create a new Lbug database handle. Creates an in-memory
|
||||
## database by default, but writes to disk if a +path+ is supplied.
|
||||
|
||||
result = new KuzuDatabase
|
||||
result = new LbugDatabase
|
||||
result.config = config
|
||||
|
||||
if path != "" and path != ":memory:":
|
||||
|
|
@ -44,13 +44,13 @@ proc newKuzuDatabase*( path="", config=kuzuConfig() ): KuzuDatabase =
|
|||
result.path = "(in-memory)"
|
||||
result.kind = memory
|
||||
|
||||
result.handle = kuzu_database()
|
||||
result.handle = lbug_database()
|
||||
|
||||
if result.kind == disk:
|
||||
result.validateDatabase()
|
||||
|
||||
if kuzu_database_init( path, config, addr result.handle ) == KuzuSuccess:
|
||||
if lbug_database_init( path, config, addr result.handle ) == LbugSuccess:
|
||||
result.valid = true
|
||||
else:
|
||||
raise newException( KuzuException, "Unable to open database." )
|
||||
raise newException( LbugException, "Unable to open database." )
|
||||
|
||||
197
src/lbug/queries.nim
Normal file
197
src/lbug/queries.nim
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
proc `=destroy`*( query: LbugQueryResultObj ) =
|
||||
## Graceful cleanup for out of scope query objects.
|
||||
if query.valid:
|
||||
when defined( debug ): echo &"Destroying query: {query}"
|
||||
lbug_query_result_destroy( addr query.handle )
|
||||
|
||||
|
||||
# Forward declarations.
|
||||
func hasNextSet( query: LbugQueryResult ): bool
|
||||
func getNextSet( query: LbugQueryResult ): LbugQueryResult
|
||||
|
||||
|
||||
func getQueryMetadata( query: LbugQueryResult, getAllQueryResults=false ) =
|
||||
## Find and retain additional data for the query.
|
||||
query.num_columns = lbug_query_result_get_num_columns( addr query.handle )
|
||||
query.num_tuples = lbug_query_result_get_num_tuples( addr query.handle )
|
||||
|
||||
# Summary information.
|
||||
var summary: lbug_query_summary
|
||||
discard lbug_query_result_get_query_summary( addr query.handle, addr summary )
|
||||
query.compile_time = lbug_query_summary_get_compiling_time( addr summary )
|
||||
query.execution_time = lbug_query_summary_get_execution_time( addr summary )
|
||||
lbug_query_summary_destroy( addr summary )
|
||||
|
||||
# Pull any additional query results.
|
||||
query.sets = @[]
|
||||
if getAllQueryResults:
|
||||
while query.hasNextSet:
|
||||
query.sets.add( query.getNextSet )
|
||||
|
||||
# Column information.
|
||||
query.column_types = @[]
|
||||
query.column_names = @[]
|
||||
if query.num_columns == 0: return
|
||||
for idx in ( 0 .. query.num_columns-1 ):
|
||||
|
||||
# types
|
||||
#
|
||||
var logical_type: lbug_logical_type
|
||||
discard lbug_query_result_get_column_data_type(
|
||||
addr query.handle,
|
||||
idx,
|
||||
addr logical_type
|
||||
)
|
||||
query.column_types.add( lbug_data_type_get_id( addr logical_type ))
|
||||
lbug_data_type_destroy( addr logical_type )
|
||||
|
||||
# names
|
||||
#
|
||||
var name: cstring
|
||||
discard lbug_query_result_get_column_name(
|
||||
addr query.handle,
|
||||
idx,
|
||||
addr name
|
||||
)
|
||||
query.column_names.add( $name )
|
||||
lbug_destroy_string( name )
|
||||
|
||||
|
||||
func hasNextSet( query: LbugQueryResult ): bool =
|
||||
## Returns +true+ if there are more result sets to be consumed.
|
||||
result = lbug_query_result_has_next_query_result( addr query.handle )
|
||||
|
||||
|
||||
func getNextSet( query: LbugQueryResult ): LbugQueryResult =
|
||||
## Consume and return the next query set result, or raise a LbugIterationError
|
||||
## if at the end of sets.
|
||||
result = new LbugQueryResult
|
||||
if lbug_query_result_get_next_query_result( addr query.handle, addr result.handle ) == LbugSuccess:
|
||||
result.valid = true
|
||||
result.getQueryMetadata()
|
||||
else:
|
||||
raise newException( LbugIterationError, &"Query iteration past end of set." )
|
||||
|
||||
|
||||
func query*( conn: LbugConnection, query: string ): LbugQueryResult =
|
||||
## Perform a database +query+ and return the result.
|
||||
result = new LbugQueryResult
|
||||
|
||||
if lbug_connection_query( addr conn.handle, query, addr result.handle ) == LbugSuccess:
|
||||
result.valid = true
|
||||
result.getQueryMetadata( getAllQueryResults=true )
|
||||
else:
|
||||
var err = lbug_query_result_get_error_message( addr result.handle )
|
||||
raise newException( LbugQueryError, &"Error running query: {err}" )
|
||||
|
||||
|
||||
proc `=destroy`*( prepared: LbugPreparedStatementObj ) =
|
||||
## Graceful cleanup for out of scope prepared objects.
|
||||
if prepared.valid:
|
||||
when defined( debug ): echo &"Destroying prepared statement: {prepared}"
|
||||
lbug_prepared_statement_destroy( addr prepared.handle )
|
||||
|
||||
|
||||
func prepare*( conn: LbugConnection, query: string ): LbugPreparedStatement =
|
||||
## Return a prepared statement that can avoid planning for repeat calls,
|
||||
## with optional variable binding via #execute.
|
||||
result = new LbugPreparedStatement
|
||||
if lbug_connection_prepare( addr conn.handle, query, addr result.handle ) == LbugSuccess:
|
||||
result.conn = conn
|
||||
result.valid = true
|
||||
else:
|
||||
var err = lbug_prepared_statement_get_error_message( addr result.handle )
|
||||
raise newException( LbugQueryError, &"Error preparing statement: {err}" )
|
||||
|
||||
|
||||
func bindValue[T](
|
||||
stmtHandle: lbug_prepared_statement,
|
||||
key: cstring,
|
||||
val: T
|
||||
) =
|
||||
## Bind a key/value to a prepared statement handle.
|
||||
when typeOf( val ) is bool:
|
||||
assert( lbug_prepared_statement_bind_bool( addr stmtHandle, key, val ) == LbugSuccess )
|
||||
elif typeOf( val ) is int8:
|
||||
assert( lbug_prepared_statement_bind_int8( addr stmtHandle, key, val ) == LbugSuccess )
|
||||
elif typeOf( val ) is int16:
|
||||
assert( lbug_prepared_statement_bind_int16( addr stmtHandle, key, val ) == LbugSuccess )
|
||||
elif typeOf( val ) is int64:
|
||||
assert( lbug_prepared_statement_bind_int64( addr stmtHandle, key, val ) == LbugSuccess )
|
||||
elif typeOf( val ) is int or typeOf( val ) is int32:
|
||||
assert( lbug_prepared_statement_bind_int32( addr stmtHandle, key, val.int32 ) == LbugSuccess )
|
||||
elif typeOf( val ) is uint8:
|
||||
assert( lbug_prepared_statement_bind_uint8( addr stmtHandle, key, val ) == LbugSuccess )
|
||||
elif typeOf( val ) is uint16:
|
||||
assert( lbug_prepared_statement_bind_uint16( addr stmtHandle, key, val ) == LbugSuccess )
|
||||
elif typeOf( val ) is uint64:
|
||||
assert( lbug_prepared_statement_bind_uint64( addr stmtHandle, key, val ) == LbugSuccess )
|
||||
elif typeOf( val ) is uint or typeOf( val ) is uint32:
|
||||
assert( lbug_prepared_statement_bind_uint32( addr stmtHandle, key, val.uint32 ) == LbugSuccess )
|
||||
elif typeOf( val ) is float:
|
||||
assert( lbug_prepared_statement_bind_double( addr stmtHandle, key, val ) == LbugSuccess )
|
||||
elif typeOf( val ) is string:
|
||||
# Fallback to string. For custom types, just cast in the cypher query.
|
||||
assert( lbug_prepared_statement_bind_string( addr stmtHandle, key, val.cstring ) == LbugSuccess )
|
||||
else:
|
||||
raise newException( LbugTypeError, &"""Unsupported type {$typeOf(val)} for prepared statement.""" )
|
||||
|
||||
|
||||
proc execute*(
|
||||
prepared: LbugPreparedStatement,
|
||||
params: tuple = ()
|
||||
): LbugQueryResult =
|
||||
## Bind variables in *params* to the statement, and return
|
||||
## a LbugQueryResult.
|
||||
|
||||
result = new LbugQueryResult
|
||||
|
||||
for key, val in params.fieldPairs:
|
||||
prepared.handle.bindValue( key, val )
|
||||
|
||||
if lbug_connection_execute(
|
||||
addr prepared.conn.handle,
|
||||
addr prepared.handle,
|
||||
addr result.handle
|
||||
) == LbugSuccess:
|
||||
result.valid = false
|
||||
result.getQueryMetadata()
|
||||
else:
|
||||
var err = lbug_query_result_get_error_message( addr result.handle )
|
||||
raise newException( LbugQueryError, &"Error executing prepared statement: {err}" )
|
||||
|
||||
|
||||
func `$`*( query: LbugQueryResult ): string =
|
||||
## Return the entire result set as a string.
|
||||
result = $lbug_query_result_to_string( addr query.handle )
|
||||
|
||||
|
||||
func hasNext*( query: LbugQueryResult ): bool =
|
||||
## Returns +true+ if there are more tuples to be consumed.
|
||||
result = lbug_query_result_has_next( addr query.handle )
|
||||
|
||||
|
||||
func getNext*( query: LbugQueryResult ): LbugFlatTuple =
|
||||
## Consume and return the next tuple result, or raise a LbugIterationError
|
||||
## if at the end of the result tuples.
|
||||
result = new LbugFlatTuple
|
||||
if lbug_query_result_get_next( addr query.handle, addr result.handle ) == LbugSuccess:
|
||||
result.valid = true
|
||||
result.num_columns = query.num_columns
|
||||
else:
|
||||
raise newException( LbugIterationError, &"Query iteration past end of tuples." )
|
||||
|
||||
|
||||
func rewind*( query: LbugQueryResult ) =
|
||||
## Reset query iteration back to the beginning.
|
||||
lbug_query_result_reset_iterator( addr query.handle )
|
||||
|
||||
|
||||
iterator items*( query: LbugQueryResult ): LbugFlatTuple =
|
||||
## Iterate available tuples, yielding to the block.
|
||||
while query.hasNext:
|
||||
yield query.getNext
|
||||
query.rewind
|
||||
|
||||
30
src/lbug/tuple.nim
Normal file
30
src/lbug/tuple.nim
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
# NOTE: Constructor in queries.nim, #getNext
|
||||
|
||||
proc `=destroy`*( tpl: LbugFlatTupleObj ) =
|
||||
## Graceful cleanup for out of scope tuples.
|
||||
if tpl.valid:
|
||||
when defined( debug ): echo &"Destroying tuple: {tpl}"
|
||||
lbug_flat_tuple_destroy( addr tpl.handle )
|
||||
|
||||
|
||||
func `$`*( tpl: LbugFlatTuple ): string =
|
||||
## Stringify a tuple.
|
||||
result = $lbug_flat_tuple_to_string( addr tpl.handle )
|
||||
result.removeSuffix( "\n" )
|
||||
|
||||
|
||||
func `[]`*( tpl: LbugFlatTuple, idx: int|uint64 ): LbugValue =
|
||||
## Returns a LbugValue at the given *idx*.
|
||||
|
||||
result = new LbugValue
|
||||
|
||||
if lbug_flat_tuple_get_value( addr tpl.handle, idx.uint64, addr result.handle ) == LbugSuccess:
|
||||
result.valid = true
|
||||
result.getType()
|
||||
else:
|
||||
raise newException( LbugIndexError,
|
||||
&"Unable to fetch tuple value at idx {idx}. ({tpl.num_columns} column(s).)" )
|
||||
|
||||
|
||||
61
src/lbug/types.nim
Normal file
61
src/lbug/types.nim
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
type
|
||||
LbugDBType* = enum
|
||||
disk, memory
|
||||
|
||||
LbugDatabaseObj = object
|
||||
handle: lbug_database
|
||||
path*: string
|
||||
kind*: LbugDBType
|
||||
config*: lbug_system_config
|
||||
valid = false
|
||||
LbugDatabase* = ref LbugDatabaseObj
|
||||
|
||||
LbugConnectionObj = object
|
||||
handle: lbug_connection
|
||||
valid = false
|
||||
LbugConnection* = ref LbugConnectionObj
|
||||
|
||||
LbugQueryResultObj = object
|
||||
handle: lbug_query_result
|
||||
num_columns*: uint64 = 0
|
||||
num_tuples*: uint64 = 0
|
||||
compile_time*: cdouble = 0
|
||||
execution_time*: cdouble = 0
|
||||
column_types*: seq[ lbug_data_type_id ]
|
||||
column_names*: seq[ string ]
|
||||
sets*: seq[ LbugQueryResult ]
|
||||
valid = false
|
||||
LbugQueryResult* = ref LbugQueryResultObj
|
||||
|
||||
LbugPreparedStatementObj = object
|
||||
handle: lbug_prepared_statement
|
||||
conn: LbugConnection
|
||||
valid = false
|
||||
LbugPreparedStatement* = ref LbugPreparedStatementObj
|
||||
|
||||
LbugFlatTupleObj = object
|
||||
handle: lbug_flat_tuple
|
||||
num_columns: uint64 = 0
|
||||
valid = false
|
||||
LbugFlatTuple* = ref LbugFlatTupleObj
|
||||
|
||||
LbugValueObj = object
|
||||
handle: lbug_value
|
||||
valid = false
|
||||
kind*: lbug_data_type_id
|
||||
LbugValue* = ref LbugValueObj
|
||||
|
||||
LbugStructValueObj = object
|
||||
value: LbugValue
|
||||
len*: uint64
|
||||
keys*: seq[ string ]
|
||||
LbugStructValue* = ref LbugStructValueObj
|
||||
|
||||
LbugException* = object of CatchableError
|
||||
LbugQueryError* = object of LbugException
|
||||
LbugIndexError* = object of LbugException
|
||||
LbugIterationError* = object of LbugException
|
||||
LbugTypeError* = object of LbugException
|
||||
|
||||
197
src/lbug/value.nim
Normal file
197
src/lbug/value.nim
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
# NOTE: Constructor in tuples.nim, #[]
|
||||
|
||||
proc `=destroy`*( value: LbugValueObj ) =
|
||||
## Graceful cleanup for out of scope values.
|
||||
if value.valid:
|
||||
when defined( debug ): echo &"Destroying value: {value}"
|
||||
lbug_value_destroy( addr value.handle )
|
||||
|
||||
|
||||
func getType( value: LbugValue ) =
|
||||
## Find and set the native Lbug type of this value.
|
||||
var logical_type: lbug_logical_type
|
||||
lbug_value_get_data_type( addr value.handle, addr logical_type )
|
||||
value.kind = lbug_data_type_get_id( addr logical_type )
|
||||
lbug_data_type_destroy( addr logical_type )
|
||||
|
||||
|
||||
template checkType( kind: lbug_data_type_id, valid_types: set ) =
|
||||
## Raises a LbugTypeError if the type conversion is incompatible.
|
||||
if kind notin valid_types:
|
||||
let msg = "Mismatched types: " & $kind & " != " & $valid_types
|
||||
raise newException( LbugTypeError, msg )
|
||||
|
||||
|
||||
func `$`*( value: LbugValue ): string =
|
||||
## Stringify a value.
|
||||
result = $lbug_value_to_string( addr value.handle )
|
||||
|
||||
|
||||
func toBool*( value: LbugValue ): bool =
|
||||
## Conversion from Lbug type to Nim.
|
||||
checkType( value.kind, {LBUG_BOOL} )
|
||||
assert( lbug_value_get_bool( addr value.handle, addr result ) == LbugSuccess )
|
||||
|
||||
|
||||
func toInt8*( value: LbugValue ): int8 =
|
||||
## Conversion from Lbug type to Nim.
|
||||
checkType( value.kind, {LBUG_INT8} )
|
||||
assert( lbug_value_get_int8( addr value.handle, addr result ) == LbugSuccess )
|
||||
|
||||
|
||||
func toInt16*( value: LbugValue ): int16 =
|
||||
## Conversion from Lbug type to Nim.
|
||||
checkType( value.kind, {LBUG_INT16} )
|
||||
assert( lbug_value_get_int16( addr value.handle, addr result ) == LbugSuccess )
|
||||
|
||||
|
||||
func toInt32*( value: LbugValue ): int32 =
|
||||
## Conversion from Lbug type to Nim.
|
||||
checkType( value.kind, {LBUG_INT32} )
|
||||
assert( lbug_value_get_int32( addr value.handle, addr result ) == LbugSuccess )
|
||||
|
||||
|
||||
func toInt64*( value: LbugValue ): int64 =
|
||||
## Conversion from Lbug type to Nim.
|
||||
checkType( value.kind, {LBUG_INT64} )
|
||||
assert( lbug_value_get_int64( addr value.handle, addr result ) == LbugSuccess )
|
||||
|
||||
|
||||
func toUint8*( value: LbugValue ): uint8 =
|
||||
## Conversion from Lbug type to Nim.
|
||||
checkType( value.kind, {LBUG_UINT8} )
|
||||
assert( lbug_value_get_uint8( addr value.handle, addr result ) == LbugSuccess )
|
||||
|
||||
|
||||
func toUint16*( value: LbugValue ): uint16 =
|
||||
## Conversion from Lbug type to Nim.
|
||||
checkType( value.kind, {LBUG_UINT16} )
|
||||
assert( lbug_value_get_uint16( addr value.handle, addr result ) == LbugSuccess )
|
||||
|
||||
|
||||
func toUint32*( value: LbugValue ): uint32 =
|
||||
## Conversion from Lbug type to Nim.
|
||||
checkType( value.kind, {LBUG_UINT32} )
|
||||
assert( lbug_value_get_uint32( addr value.handle, addr result ) == LbugSuccess )
|
||||
|
||||
|
||||
func toUint64*( value: LbugValue ): uint64 =
|
||||
## Conversion from Lbug type to Nim.
|
||||
checkType( value.kind, {LBUG_UINT64} )
|
||||
assert( lbug_value_get_uint64( addr value.handle, addr result ) == LbugSuccess )
|
||||
|
||||
|
||||
func toDouble*( value: LbugValue ): float =
|
||||
## Conversion from Lbug type to Nim.
|
||||
checkType( value.kind, {LBUG_DOUBLE} )
|
||||
assert( lbug_value_get_double( addr value.handle, addr result ) == LbugSuccess )
|
||||
|
||||
|
||||
func toFloat*( value: LbugValue ): float =
|
||||
## Conversion from Lbug type to Nim.
|
||||
checkType( value.kind, {LBUG_FLOAT} )
|
||||
var rv: cfloat
|
||||
assert( lbug_value_get_float( addr value.handle, addr rv ) == LbugSuccess )
|
||||
result = rv
|
||||
|
||||
|
||||
func toTimestamp*( value: LbugValue ): int =
|
||||
## Conversion from Lbug type to Nim.
|
||||
checkType( value.kind, {LBUG_TIMESTAMP} )
|
||||
var rv: lbug_timestamp_t
|
||||
assert( lbug_value_get_timestamp( addr value.handle, addr rv ) == LbugSuccess )
|
||||
result = rv.value
|
||||
|
||||
|
||||
func toList*( value: LbugValue ): seq[ LbugValue ] =
|
||||
## Return a sequence from LBUG_LIST values.
|
||||
checkType( value.kind, {LBUG_LIST} )
|
||||
result = @[]
|
||||
var size: uint64
|
||||
assert( lbug_value_get_list_size( addr value.handle, addr size ) == LbugSuccess )
|
||||
if size == 0: return
|
||||
|
||||
for i in ( 0 .. size-1 ):
|
||||
var kval = new LbugValue
|
||||
assert(
|
||||
lbug_value_get_list_element(
|
||||
addr value.handle, i.uint64, addr kval.handle
|
||||
) == LbugSuccess )
|
||||
kval.getType()
|
||||
result.add( kval )
|
||||
|
||||
const toSeq* = toList
|
||||
|
||||
|
||||
func toBlob*( value: LbugValue ): seq[ byte ] =
|
||||
## Conversion from Lbug type to Nim - returns a BLOB as a sequence of bytes.
|
||||
checkType( value.kind, {LBUG_BLOB} )
|
||||
|
||||
result = @[]
|
||||
var data: ptr byte
|
||||
assert( lbug_value_get_blob( addr value.handle, addr data ) == LbugSuccess )
|
||||
|
||||
for idx in 0 .. BLOB_MAXSIZE:
|
||||
var byte = cast[ptr byte](cast[uint](data) + idx.uint)[]
|
||||
if byte == 0: break
|
||||
result.add( byte )
|
||||
|
||||
lbug_destroy_blob( data )
|
||||
|
||||
|
||||
func toStruct*( value: LbugValue ): LbugStructValue =
|
||||
## Create a convenience class for struct-like LbugValues.
|
||||
checkType( value.kind, {
|
||||
LBUG_STRUCT,
|
||||
LBUG_NODE,
|
||||
LBUG_REL,
|
||||
LBUG_RECURSIVE_REL,
|
||||
LBUG_UNION
|
||||
})
|
||||
result = new LbugStructValue
|
||||
result.value = value
|
||||
|
||||
discard lbug_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(
|
||||
lbug_value_get_struct_field_name(
|
||||
addr value.handle, idx.uint64, addr keyname
|
||||
) == LbugSuccess )
|
||||
result.keys.add( $keyname )
|
||||
|
||||
const toNode* = toStruct
|
||||
const toRel* = toStruct
|
||||
|
||||
|
||||
func `[]`*( struct: LbugStructValue, key: string ): LbugValue =
|
||||
## Return a LbugValue 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( LbugIndexError,
|
||||
&"""No such struct key "{key}".""" )
|
||||
|
||||
result = new LbugValue
|
||||
assert(
|
||||
lbug_value_get_struct_field_value(
|
||||
addr struct.value.handle, idx.uint64, addr result.handle
|
||||
) == LbugSuccess )
|
||||
result.getType()
|
||||
|
||||
|
||||
func `$`*( struct: LbugStructValue ): string =
|
||||
## Stringify a struct value.
|
||||
result = $lbug_value_to_string( addr struct.value.handle )
|
||||
|
||||
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
|
||||
assert db.path == "(in-memory)"
|
||||
assert typeOf( db.connect ) is KuzuConnection
|
||||
assert typeOf( db.connect ) is LbugConnection
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
# FIXME: This test should really perform some
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
# There is currently no getter for this, so
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
assert typeOf( KUZU_DEFAULT_CONFIG ) is kuzu_system_config
|
||||
assert typeOf( LBUG_DEFAULT_CONFIG ) is lbug_system_config
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import re
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
assert KUZU_VERSION.contains( re"^\d+\.\d+\.\d+$" )
|
||||
assert LBUG_VERSION.contains( re"^\d+\.\d+\.\d+$" )
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import re
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let version = $kuzuGetVersion()
|
||||
let version = $lbugGetVersion()
|
||||
assert version.contains( re"^\d+\.\d+\.\d+(?:\.\d+)?$" )
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
assert kuzuGetStorageVersion() >= 36
|
||||
assert lbugGetStorageVersion() >= 36
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
var db = newKuzuDatabase()
|
||||
var db = newLbugDatabase()
|
||||
assert db.path == "(in-memory)"
|
||||
assert db.kind == memory
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import
|
|||
std/files,
|
||||
std/paths
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
const DATABASE_PATH = Path( "tmp/testdb" )
|
||||
DATABASE_PATH.removeFile()
|
||||
|
||||
var db = newKuzuDatabase( $DATABASE_PATH, kuzuConfig( auto_checkpoint=false ) )
|
||||
var db = newLbugDatabase( $DATABASE_PATH, lbugConfig( auto_checkpoint=false ) )
|
||||
assert db.path == "tmp/testdb"
|
||||
assert db.config.auto_checkpoint == false
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import
|
|||
std/paths,
|
||||
std/re
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
const NOT_A_DATABASE_PATH = Path( "tmp/not-a-db" )
|
||||
|
||||
|
|
@ -15,9 +15,9 @@ fh.write( "Hi." )
|
|||
fh.close
|
||||
|
||||
try:
|
||||
discard newKuzuDatabase( $NOT_A_DATABASE_PATH )
|
||||
except KuzuException as err:
|
||||
assert err.msg.contains( re"""Unable to open database: "tmp/not-a-db" Doesn't appear to be a Kuzu file""" )
|
||||
discard newLbugDatabase( $NOT_A_DATABASE_PATH )
|
||||
except LbugException as err:
|
||||
assert err.msg.contains( re"""Unable to open database: "tmp/not-a-db" Doesn't appear to be a LadybugDB file""" )
|
||||
|
||||
NOT_A_DATABASE_PATH.removeFile()
|
||||
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@ import
|
|||
std/files,
|
||||
std/paths
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
const DATABASE_PATH = Path( "tmp/testdb" )
|
||||
|
||||
DATABASE_PATH.removeFile()
|
||||
var db = newKuzuDatabase( $DATABASE_PATH )
|
||||
var db = newLbugDatabase( $DATABASE_PATH )
|
||||
|
||||
assert db.path == $DATABASE_PATH
|
||||
assert db.kind == disk
|
||||
assert db.config == kuzuConfig()
|
||||
assert db.config == lbugConfig()
|
||||
assert db.config.read_only == false
|
||||
|
||||
DATABASE_PATH.removeFile()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query """
|
||||
|
|
|
|||
|
|
@ -4,19 +4,19 @@ discard """
|
|||
output: "d.thing\nCamel\nLampshade\nDelicious Cake\n"
|
||||
"""
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
|
||||
var p = conn.prepare( "CREATE (d:Doop {thing: $thing})" )
|
||||
assert typeOf( p ) is KuzuPreparedStatement
|
||||
assert typeOf( p ) is LbugPreparedStatement
|
||||
|
||||
for thing in @[ "Camel", "Lampshade", "Delicious Cake" ]:
|
||||
q = p.execute( (thing: thing) )
|
||||
assert typeOf( q ) is KuzuQueryResult
|
||||
assert typeOf( q ) is LbugQueryResult
|
||||
|
||||
# Fixed post v0.8.2:
|
||||
# https://github.com/kuzudb/kuzu/issues/5102
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ discard """
|
|||
output: "0|-222222|128|True|Stuff!|3.344903|239.299923|a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11|2025-03-29"
|
||||
"""
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( """CREATE NODE TABLE Doop (
|
||||
|
|
@ -21,7 +21,7 @@ var q = conn.query( """CREATE NODE TABLE Doop (
|
|||
date DATE,
|
||||
PRIMARY KEY(id)
|
||||
)""" )
|
||||
assert typeOf( q ) is KuzuQueryResult
|
||||
assert typeOf( q ) is LbugQueryResult
|
||||
|
||||
|
||||
var stmt = conn.prepare( """CREATE (d:Doop {
|
||||
|
|
@ -34,7 +34,7 @@ var stmt = conn.prepare( """CREATE (d:Doop {
|
|||
uuid: UUID($uuid),
|
||||
date: DATE($date)
|
||||
})""" )
|
||||
assert typeOf( stmt ) is KuzuPreparedStatement
|
||||
assert typeOf( stmt ) is LbugPreparedStatement
|
||||
|
||||
|
||||
q = stmt.execute((
|
||||
|
|
@ -47,7 +47,7 @@ q = stmt.execute((
|
|||
uuid: "A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11",
|
||||
date: "2025-03-29"
|
||||
))
|
||||
assert typeOf( q ) is KuzuQueryResult
|
||||
assert typeOf( q ) is LbugQueryResult
|
||||
|
||||
|
||||
q = conn.query( "MATCH (d:Doop) RETURN d.*" )
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ discard """
|
|||
output: "a\nb\nc\nd\ne\nf\n"
|
||||
"""
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "RETURN 'hi'" )
|
||||
|
||||
assert typeOf( q ) is KuzuQueryResult
|
||||
assert typeOf( q ) is LbugQueryResult
|
||||
assert q.sets.len == 0
|
||||
|
||||
q = conn.query """
|
||||
|
|
@ -23,7 +23,7 @@ q = conn.query """
|
|||
RETURN "f";
|
||||
"""
|
||||
|
||||
assert typeOf( q ) is KuzuQueryResult
|
||||
assert typeOf( q ) is LbugQueryResult
|
||||
assert q.sets.len == 5
|
||||
|
||||
echo q.getNext
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
assert typeOf( q ) is KuzuQueryResult
|
||||
assert typeOf( q ) is LbugQueryResult
|
||||
|
||||
for thing in @[ "Camel", "Lampshade", "Delicious Cake" ]:
|
||||
q = conn.query( "CREATE (d:Doop {thing: '" & thing & "'})" )
|
||||
assert typeOf( q ) is KuzuQueryResult
|
||||
assert typeOf( q ) is LbugQueryResult
|
||||
|
||||
q = conn.query( "MATCH (d:Doop) RETURN d.thing" )
|
||||
assert q.num_columns == 1
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ discard """
|
|||
output: "Camel\nLampshade\nDelicious Cake\n"
|
||||
"""
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ discard """
|
|||
output: "Camel\nLampshade\nCamel\nLampshade\n"
|
||||
"""
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ discard """
|
|||
output: "d.thing\nokay!\n\n"
|
||||
"""
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
assert typeOf( q ) is KuzuQueryResult
|
||||
assert typeOf( q ) is LbugQueryResult
|
||||
|
||||
q = conn.query( "MATCH (d:Doop) RETURN d.thing" )
|
||||
assert q.num_tuples == 0
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
assert typeOf( q ) is KuzuQueryResult
|
||||
assert typeOf( q ) is LbugQueryResult
|
||||
|
||||
q = conn.query( "CREATE (d:Doop {thing: 'okay!'})" )
|
||||
q = conn.query( "MATCH (d:Doop) RETURN d.id AS IDENTIFIER, d.thing AS THING" )
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
assert typeOf( q ) is KuzuQueryResult
|
||||
assert typeOf( q ) is LbugQueryResult
|
||||
|
||||
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"
|
||||
assert $q.column_types[0] == "LBUG_SERIAL"
|
||||
assert $q.column_types[1] == "LBUG_STRING"
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
import
|
||||
std/re
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
try:
|
||||
discard conn.query( "NOPE NOPE NOPE" )
|
||||
except KuzuQueryError as err:
|
||||
except LbugQueryError as err:
|
||||
assert err.msg.contains( re"""Parser exception: extraneous input 'NOPE'""" )
|
||||
|
||||
|
|
|
|||
|
|
@ -2,30 +2,30 @@
|
|||
|
||||
import
|
||||
std/re
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, created DATE, PRIMARY KEY(id) )" )
|
||||
assert typeOf( q ) is KuzuQueryResult
|
||||
assert typeOf( q ) is LbugQueryResult
|
||||
|
||||
var p = conn.prepare( "CREATE (d:Doop {created: $created})" )
|
||||
assert typeOf( p ) is KuzuPreparedStatement
|
||||
assert typeOf( p ) is LbugPreparedStatement
|
||||
|
||||
# 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.""" )
|
||||
except LbugQueryError as err:
|
||||
assert err.msg.contains( re"""Conversion exception: Error occurred during parsing 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:
|
||||
except LbugQueryError as err:
|
||||
assert err.msg.contains( re"""Given: "1111-1111". Expected format: \(YYYY-MM-DD\)""" )
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,19 +2,19 @@
|
|||
|
||||
import
|
||||
std/re
|
||||
import kuzu
|
||||
import ladybug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
|
||||
var p = conn.prepare( "CREATE (d:Doop {thing: $thing})" )
|
||||
assert typeOf( p ) is KuzuPreparedStatement
|
||||
assert typeOf( p ) is LbugPreparedStatement
|
||||
|
||||
try:
|
||||
discard p.execute( (nope: "undefined var in statement!") )
|
||||
except KuzuQueryError as err:
|
||||
discard p.execute( (nope: "undefined var in statement!", thing: "yep") )
|
||||
except LbugQueryError as err:
|
||||
assert err.msg.contains( re"""Parameter nope not found.""" )
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,19 +2,19 @@
|
|||
|
||||
import
|
||||
std/re
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
|
||||
var p = conn.prepare( "CREAET (d:Doop {thing: $thing})" )
|
||||
assert typeOf( p ) is KuzuPreparedStatement
|
||||
assert typeOf( p ) is LbugPreparedStatement
|
||||
|
||||
try:
|
||||
discard p.execute
|
||||
except KuzuQueryError as err:
|
||||
except LbugQueryError as err:
|
||||
assert err.msg.contains( re"""Parser exception: extraneous input 'CREAET'""" )
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ discard """
|
|||
output: "okay!"
|
||||
"""
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
import
|
||||
std/re
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
|
|
@ -12,6 +12,6 @@ q = conn.query( "MATCH (d:Doop) RETURN d.thing" )
|
|||
|
||||
try:
|
||||
discard q.getNext
|
||||
except KuzuIterationError as err:
|
||||
except LbugIterationError as err:
|
||||
assert err.msg.contains( re"""Query iteration past end.""" )
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
import
|
||||
std/re
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
|
|
@ -15,6 +15,6 @@ let tup = q.getNext
|
|||
|
||||
try:
|
||||
echo tup[22]
|
||||
except KuzuIndexError as err:
|
||||
except LbugIndexError as err:
|
||||
assert err.msg.contains( re"""Unable to fetch tuple value at idx 22.""" )
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ discard """
|
|||
output: "okay!"
|
||||
"""
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( """CREATE NODE TABLE Doop (
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
var db = newKuzuDatabase()
|
||||
var db = newLbugDatabase()
|
||||
var conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doot ( id SERIAL, data BLOB, PRIMARY KEY(id) )" )
|
||||
|
|
@ -16,12 +16,12 @@ 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.kind == LBUG_BLOB
|
||||
assert val.toBlob == expected
|
||||
|
||||
expected = @[72, 101, 108, 108, 111, 33]
|
||||
val = q.getNext[0]
|
||||
assert val.kind == KUZU_BLOB
|
||||
assert val.kind == LBUG_BLOB
|
||||
assert val.toBlob == expected
|
||||
|
||||
var str: string
|
||||
|
|
|
|||
|
|
@ -1,39 +1,39 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
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
|
||||
assert list.kind == LBUG_LIST
|
||||
|
||||
var items = list.toSeq
|
||||
assert items.len == 5
|
||||
assert typeOf( items ) is seq[KuzuValue]
|
||||
assert typeOf( items ) is seq[LbugValue]
|
||||
|
||||
for i in items:
|
||||
assert( i.kind == KUZU_INT64 )
|
||||
assert( i.kind == LBUG_INT64 )
|
||||
|
||||
|
||||
q = conn.query( """RETURN ["woo", "hoo"] AS list""" )
|
||||
list = q.getNext[0]
|
||||
assert list.kind == KUZU_LIST
|
||||
assert list.kind == LBUG_LIST
|
||||
|
||||
items = list.toSeq
|
||||
assert items.len == 2
|
||||
assert typeOf( items ) is seq[KuzuValue]
|
||||
assert typeOf( items ) is seq[LbugValue]
|
||||
|
||||
for i in items:
|
||||
assert( i.kind == KUZU_STRING )
|
||||
assert( i.kind == LBUG_STRING )
|
||||
|
||||
|
||||
q = conn.query( """RETURN [] AS list""" )
|
||||
list = q.getNext[0]
|
||||
assert list.kind == KUZU_LIST
|
||||
assert list.kind == LBUG_LIST
|
||||
|
||||
items = list.toList
|
||||
assert items.len == 0
|
||||
assert typeOf( items ) is seq[KuzuValue]
|
||||
assert typeOf( items ) is seq[LbugValue]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( """RETURN 12""" )
|
||||
try:
|
||||
discard q.getNext[0].toStruct
|
||||
except KuzuTypeError:
|
||||
except LbugTypeError:
|
||||
discard
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# vim: set et sta sw=4 ts=4 :
|
||||
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
|
|
@ -15,7 +15,7 @@ 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
|
||||
assert id.kind == LBUG_INT64
|
||||
assert thing.kind == LBUG_STRING
|
||||
assert node.kind == LBUG_NODE
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
import
|
||||
std/re
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||
|
|
@ -14,12 +14,12 @@ q = conn.query( "MATCH (d:Doop) RETURN d" )
|
|||
|
||||
var tup = q.getNext
|
||||
var val = tup[0]
|
||||
assert val.kind == KUZU_NODE
|
||||
assert val.kind == LBUG_NODE
|
||||
|
||||
try:
|
||||
discard val.toInt32
|
||||
except KuzuTypeError as err:
|
||||
assert err.msg.contains( re"""Mismatched types: KUZU_NODE != {KUZU_INT32}""" )
|
||||
except LbugTypeError as err:
|
||||
assert err.msg.contains( re"""Mismatched types: LBUG_NODE != {LBUG_INT32}""" )
|
||||
|
||||
|
||||
q = conn.query( "RETURN 1" )
|
||||
|
|
@ -27,7 +27,7 @@ val = q.getNext[0]
|
|||
|
||||
try:
|
||||
discard val.toStruct
|
||||
except KuzuTypeError as err:
|
||||
assert err.msg.contains( re"""Mismatched types: KUZU_INT.* != {KUZU_NODE, KUZU_REL,.*}""" )
|
||||
except LbugTypeError as err:
|
||||
assert err.msg.contains( re"""Mismatched types: LBUG_INT.* != {LBUG_NODE, LBUG_REL,.*}""" )
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
import
|
||||
std/re
|
||||
import kuzu
|
||||
import lbug
|
||||
|
||||
let db = newKuzuDatabase()
|
||||
let db = newLbugDatabase()
|
||||
let conn = db.connect
|
||||
|
||||
var q = conn.query( """RETURN {test1: 1, test2: "bewts"} AS struct""" )
|
||||
|
|
@ -15,7 +15,7 @@ assert struct.keys == @["test1", "test2"]
|
|||
|
||||
try:
|
||||
discard struct["nope"]
|
||||
except KuzuIndexError as err:
|
||||
except LbugIndexError as err:
|
||||
assert err.msg.contains( re"""No such struct key "nope"""" )
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue