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>
|
## 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
|
home
|
||||||
: https://code.martini.nu/mahlon/nim-kuzu
|
: https://code.martini.nu/mahlon/nim-ladybug
|
||||||
|
|
||||||
github_mirror
|
github_mirror
|
||||||
: https://github.com/mahlonsmith/nim-kuzu
|
: https://github.com/mahlonsmith/nim-ladybug
|
||||||
|
|
||||||
|
|
||||||
## Description
|
## 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
|
optimized for handling complex join-heavy analytical workloads on very large
|
||||||
graphs, with the following core feature set:
|
graphs, with the following core feature set:
|
||||||
|
|
||||||
|
|
@ -25,22 +25,21 @@ graphs, with the following core feature set:
|
||||||
- Multi-core query parallelism
|
- Multi-core query parallelism
|
||||||
- Serializable ACID transactions
|
- Serializable ACID transactions
|
||||||
|
|
||||||
For more information about Kuzu itself, see its
|
For more information about Ladybug itself, see its
|
||||||
[documentation](https://docs.kuzudb.com/).
|
[documentation](https://docs.ladybugdb.com/).
|
||||||
|
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
* A functioning Nim >= 2 installation
|
* A functioning Nim >= 2 installation
|
||||||
- [KuzuDB](https://kuzudb.com) to be locally installed!
|
- [LadybugDB](https://ladybugdb.com) to be locally installed!
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
$ nimble install kuzu
|
$ nimble install ladybug
|
||||||
|
|
||||||
The current version of this library is built for Kuzu v0.11.3, which sadly
|
The current version of this library is built for Ladybug v0.12.0.
|
||||||
seems may have been the final release of kuzudb. :(
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
@ -53,11 +52,11 @@ You can also find a bunch of working examples in the tests.
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
You can check out the current development source via Git/Jujutsu at its
|
You can check out the current development source via Git/Jujutsu at its
|
||||||
[home repo](https://code.martini.nu/mahlon/nim-kuzu), or the
|
[home repo](https://code.martini.nu/mahlon/nim-ladybug), or the
|
||||||
[project mirror](https://github.com/mahlonsmith/nim-kuzu).
|
[project mirror](https://github.com/mahlonsmith/nim-ladybug).
|
||||||
|
|
||||||
After checking out the source, uncomment the development dependencies
|
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
|
$ nimble setup
|
||||||
|
|
||||||
|
|
@ -71,4 +70,5 @@ development.
|
||||||
- Mahlon E. Smith <mahlon@martini.nu>
|
- Mahlon E. Smith <mahlon@martini.nu>
|
||||||
|
|
||||||
A note of thanks to @mantielero on Github, who has a Kuzu binding for an early
|
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
|
## Prior Reading
|
||||||
|
|
||||||
If you're just starting with Kuzu or graph databases, it's probably a good idea
|
If you're just starting with Ladybug or graph databases, it's probably a good idea
|
||||||
to familiarize yourself with the [Kuzu Documentation](https://docs.kuzudb.com/)
|
to familiarize yourself with the [Ladybug Documentation](https://docs.ladybugdb.com/)
|
||||||
and the [Cypher Language](https://docs.kuzudb.com/tutorials/cypher/). This
|
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 Kuzu usage.
|
library won't do much for you by itself without a basic understanding of Ladybug usage.
|
||||||
|
|
||||||
|
|
||||||
## Checking Compatibility
|
## 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
|
shared library. As such, the version of this library might not match with what
|
||||||
you currently have installed.
|
you currently have installed.
|
||||||
|
|
||||||
Check the [README](README.md), the [History](History.md), and the following
|
Check the [README](README.md), the [History](History.md), and the following
|
||||||
table to ensure you're using the correct version for your Kuzu
|
table to ensure you're using the correct version for your Ladybug
|
||||||
installation. I'll make a modest effort for backwards compatibility while Kuzu
|
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
|
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.
|
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 |
|
| Kuzu Library Version | Nim Kuzu Minimum Version |
|
||||||
| -------------------- | ------------------------ |
|
| -------------------- | ------------------------ |
|
||||||
| v0.8.2 | v0.1.0 |
|
| 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.10.0 | v0.4.0 |
|
||||||
| v0.11.0 | v0.5.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.
|
are looking right.
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
import kuzu
|
import ladybug
|
||||||
|
|
||||||
echo KUZU_VERSION #=> "0.1.0"
|
echo LBUG_VERSION #=> "0.7.0"
|
||||||
echo kuzuGetVersion() #=> "0.8.2"
|
echo lbugGetVersion() #=> "0.12.0"
|
||||||
echo kuzuVersionCompatible() #=> true
|
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
|
## 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
|
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
|
-- it will create an empty database if the path is currently non-existent, or
|
||||||
open an existing database otherwise.
|
open an existing database otherwise.
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
# "db" is in-memory and will evaporate when the process ends.
|
# "db" is in-memory and will evaporate when the process ends.
|
||||||
var db = newKuzuDatabase()
|
var db = newLbugDatabase()
|
||||||
```
|
```
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
# "db" is persistent, stored in the file "data.kz".
|
# "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`.
|
The database path is retained, and can be recalled via `db.path`.
|
||||||
|
|
||||||
|
|
@ -86,19 +105,19 @@ if db.config.enable_compression:
|
||||||
echo "Yes!"
|
echo "Yes!"
|
||||||
```
|
```
|
||||||
|
|
||||||
You can alter configuration options when connecting by passing a `kuzuConfig`
|
You can alter configuration options when connecting by passing a `lbugConfig`
|
||||||
object as the second argument to `newKuzuDatabase()`:
|
object as the second argument to `newLbugDatabase()`:
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
# Open a readonly handle.
|
# Open a readonly handle.
|
||||||
var db = newKuzuDatabase( "data.kz", kuzuConfig( read_only=true ) )
|
var db = newLbugDatabase( "data.kz", lbugConfig( read_only=true ) )
|
||||||
```
|
```
|
||||||
|
|
||||||
### The Connection
|
### The Connection
|
||||||
|
|
||||||
All interaction with the database is performed via a connection object. There
|
All interaction with the database is performed via a connection object. There
|
||||||
are limitations to database handles and connection objects -- see the
|
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:
|
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
|
You can perform a basic query via the appropriately named `query()` function on
|
||||||
the connection. Via this method, queries are run immediately. A
|
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.
|
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:
|
headers and all tuple results:
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
|
|
@ -154,7 +173,7 @@ echo res.execution_time #=> 1.624
|
||||||
assert res.column_names == @["hi", "pin", "list"]
|
assert res.column_names == @["hi", "pin", "list"]
|
||||||
|
|
||||||
# Return the column data types as a sequence.
|
# 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
|
### Prepared Statements
|
||||||
|
|
@ -172,7 +191,7 @@ RETURN
|
||||||
[1,2,3] AS list
|
[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()
|
var res = stmt.execute()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -198,13 +217,13 @@ echo $res #=>
|
||||||
#### Type Conversion
|
#### Type Conversion
|
||||||
|
|
||||||
When binding variables to a prepared statement, most Nim types are automatically
|
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
|
```nim
|
||||||
var stmt = conn.prepare( """RETURN $num AS num""" )
|
var stmt = conn.prepare( """RETURN $num AS num""" )
|
||||||
var res = stmt.execute( (num: 12) )
|
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
|
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
|
```nim
|
||||||
var stmt = conn.prepare( """RETURN $num AS num""" )
|
var stmt = conn.prepare( """RETURN $num AS num""" )
|
||||||
var res: KuzuQueryResult
|
var res: LbugQueryResult
|
||||||
|
|
||||||
res = stmt.execute( (num: 12'u64) )
|
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) )
|
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
|
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.
|
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
|
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!
|
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.
|
the content, and it works with the majority of types.
|
||||||
|
|
||||||
An extended example:
|
An extended example:
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
import std/sequtils
|
import std/sequtils
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
var db = newKuzuDatabase()
|
var db = newLbugDatabase()
|
||||||
var conn = db.connect
|
var conn = db.connect
|
||||||
|
|
||||||
var res: KuzuQueryResult
|
var res: LbugQueryResult
|
||||||
|
|
||||||
# Create a node table.
|
# 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((
|
res = stmt.execute((
|
||||||
num: 2,
|
num: 2,
|
||||||
|
|
@ -295,26 +314,26 @@ echo $res #=>
|
||||||
# e.id|e.num|e.done|e.comment|e.karma|e.thing|e.created|e.activity
|
# 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
|
# 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 ):
|
for pair in res.column_names.zip( res.column_types ):
|
||||||
echo pair #=>
|
echo pair #=>
|
||||||
# ("e.id", KUZU_SERIAL)
|
# ("e.id", LBUG_SERIAL)
|
||||||
# ("e.num", KUZU_UINT8)
|
# ("e.num", LBUG_UINT8)
|
||||||
# ("e.done", KUZU_BOOL)
|
# ("e.done", LBUG_BOOL)
|
||||||
# ("e.comment", KUZU_STRING)
|
# ("e.comment", LBUG_STRING)
|
||||||
# ("e.karma", KUZU_DOUBLE)
|
# ("e.karma", LBUG_DOUBLE)
|
||||||
# ("e.thing", KUZU_UUID)
|
# ("e.thing", LBUG_UUID)
|
||||||
# ("e.created", KUZU_DATE)
|
# ("e.created", LBUG_DATE)
|
||||||
# ("e.activity", KUZU_TIMESTAMP)
|
# ("e.activity", LBUG_TIMESTAMP)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Reading Results
|
## 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.
|
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
|
A `LbugQueryResult` is an iterator. You can use regular Nim functions that yield
|
||||||
each `KuzuFlatTuple` -- essentially, each row that was returned in a result set.
|
each `LbugFlatTuple` -- essentially, each row that was returned in a result set.
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
var res = conn.query """
|
var res = conn.query """
|
||||||
|
|
@ -323,7 +342,7 @@ var res = conn.query """
|
||||||
RETURN items, thing
|
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:
|
for row in res:
|
||||||
echo row #=>
|
echo row #=>
|
||||||
# 1|thing
|
# 1|thing
|
||||||
|
|
@ -333,7 +352,7 @@ for row in res:
|
||||||
|
|
||||||
Once iteration has reached the end, it is automatically rewound for reuse.
|
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
|
`getNext()` after the last row results in an error. Use `hasNext()` to check
|
||||||
before calling.
|
before calling.
|
||||||
|
|
||||||
|
|
@ -350,23 +369,23 @@ if res.hasNext:
|
||||||
|
|
||||||
echo res.getNext #=> 2
|
echo res.getNext #=> 2
|
||||||
echo res.getNext #=> 3
|
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
|
## Multiple Query Results
|
||||||
|
|
||||||
A query can potentially return any number of separate statements. In the case
|
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
|
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:
|
remaining:
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
let query = conn.query """
|
let query = conn.query """
|
||||||
|
|
@ -400,8 +419,8 @@ for set in query.sets:
|
||||||
|
|
||||||
## Working with Values
|
## Working with Values
|
||||||
|
|
||||||
A `KuzuFlatTuple` contains the entire row. You can index a value at its column
|
A `LbugFlatTuple` contains the entire row. You can index a value at its column
|
||||||
position, returning a `KuzuValue`.
|
position, returning a `LbugValue`.
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
var res = conn.query """
|
var res = conn.query """
|
||||||
|
|
@ -419,27 +438,27 @@ var row = res.getNext
|
||||||
for idx in ( 0 .. res.num_columns-1 ):
|
for idx in ( 0 .. res.num_columns-1 ):
|
||||||
var value = row[idx]
|
var value = row[idx]
|
||||||
echo res.column_names[idx], ": ", value, " (", value.kind, ")" #=>
|
echo res.column_names[idx], ": ", value, " (", value.kind, ")" #=>
|
||||||
# num: 1 (KUZU_INT64)
|
# num: 1 (LBUG_INT64)
|
||||||
# done: True (KUZU_BOOL)
|
# done: True (LBUG_BOOL)
|
||||||
# comment: A comment (KUZU_STRING)
|
# comment: A comment (LBUG_STRING)
|
||||||
# karma: 12.840000 (KUZU_DOUBLE)
|
# karma: 12.840000 (LBUG_DOUBLE)
|
||||||
# thing: b41deae0-dddf-430b-981d-3fb93823e495 (KUZU_UUID)
|
# thing: b41deae0-dddf-430b-981d-3fb93823e495 (LBUG_UUID)
|
||||||
# created: 2025-03-29 (KUZU_DATE)
|
# created: 2025-03-29 (LBUG_DATE)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Types
|
### 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.
|
check what type it is via the 'kind' property.
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
var res = conn.query """RETURN "hello""""
|
var res = conn.query """RETURN "hello""""
|
||||||
var value = res.getNext[0]
|
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:
|
convert it for regular Nim usage:
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
|
|
@ -455,12 +474,12 @@ assert value.toInt64 + 1 == 2561
|
||||||
|
|
||||||
### Lists
|
### Lists
|
||||||
|
|
||||||
A `KuzuValue` of type `KUZU_LIST` can be converted to a Nim sequence of
|
A `LbugValue` of type `LBUG_LIST` can be converted to a Nim sequence of
|
||||||
`KuzuValues` with the `toList()` function:
|
`LbugValues` with the `toList()` function:
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
import std/sequtils
|
import std/sequtils
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
var res = conn.query """
|
var res = conn.query """
|
||||||
RETURN [10, 20, 30]
|
RETURN [10, 20, 30]
|
||||||
|
|
@ -471,16 +490,16 @@ var value = res.getNext[0]
|
||||||
var list = value.toList
|
var list = value.toList
|
||||||
echo list #=> @[10,20,30]
|
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
|
### Struct-like Objects
|
||||||
|
|
||||||
Various Kuzu types can act like a struct - this includes `KUZU_NODE`,
|
Various Ladybug types can act like a struct - this includes `LBUG_NODE`,
|
||||||
`KUZU_REL`, and of course an explicit `KUZU_STRUCT` itself, among others.
|
`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()`.
|
convenience, this is also aliased to `toNode()` and `toRel()`.
|
||||||
|
|
||||||
Once converted, you can access struct values by passing the key name to `[]`:
|
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
|
import
|
||||||
std/sequtils,
|
std/sequtils,
|
||||||
std/strformat
|
std/strformat
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
var db = newKuzuDatabase()
|
var db = newLbugDatabase()
|
||||||
var conn = db.connect
|
var conn = db.connect
|
||||||
|
|
||||||
var res = conn.query """
|
var res = conn.query """
|
||||||
|
|
@ -540,7 +559,7 @@ res = conn.query """
|
||||||
#
|
#
|
||||||
for row in res:
|
for row in res:
|
||||||
var since = row[0]
|
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}.""" #=>
|
echo &"""{people[0]["name"]} has known {people[1]["name"]} since {since}.""" #=>
|
||||||
# Bob has known Bruce since 2003.
|
# Bob has known Bruce since 2003.
|
||||||
# Bob has known Alice since 2009.
|
# Bob has known Alice since 2009.
|
||||||
|
|
@ -550,7 +569,7 @@ for row in res:
|
||||||
|
|
||||||
### Blobs
|
### 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.
|
using `toBlob` will return the raw sequence of bytes.
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# vim: set et sta sw=4 ts=4 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
version = "0.6.1"
|
version = "0.7.0"
|
||||||
author = "Mahlon E. Smith"
|
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"
|
license = "BSD-3-Clause"
|
||||||
srcDir = "src"
|
srcDir = "src"
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@ requires "nim ^= 2.0.0"
|
||||||
#requires "zip ^= 0.3.1"
|
#requires "zip ^= 0.3.1"
|
||||||
|
|
||||||
task makewrapper, "Generate the C wrapper using Futhark":
|
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.":
|
task test, "Run the test suite.":
|
||||||
exec "testament --megatest:off all"
|
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 README.md"
|
||||||
exec "nim md2html --project --outdir:docs History.md"
|
exec "nim md2html --project --outdir:docs History.md"
|
||||||
exec "nim md2html --project --outdir:docs USAGE.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 :
|
# 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.
|
## Graceful cleanup for an open DB handle when it goes out of scope.
|
||||||
if db.valid:
|
if db.valid:
|
||||||
when defined( debug ): echo &"Destroying database: {db}"
|
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
|
## Perform basic validity checks against an existing on disk database
|
||||||
## for better error messaging.
|
## for better error messaging.
|
||||||
|
|
||||||
|
|
@ -21,20 +21,20 @@ proc validateDatabase( db: KuzuDatabase ): void =
|
||||||
let magic = buf[0..3].join
|
let magic = buf[0..3].join
|
||||||
let storage_version = buf[4].uint
|
let storage_version = buf[4].uint
|
||||||
|
|
||||||
if magic != "KUZU":
|
if magic != "LBUG":
|
||||||
raise newException( KuzuException, "Unable to open database: " &
|
raise newException( LbugException, "Unable to open database: " &
|
||||||
&""""{db.path}" Doesn't appear to be a Kuzu file.""" )
|
&""""{db.path}" Doesn't appear to be a LadybugDB file.""" )
|
||||||
|
|
||||||
if storageVersion != kuzuGetStorageVersion():
|
if storageVersion != lbugGetStorageVersion():
|
||||||
raise newException( KuzuException, "Unable to open database: " &
|
raise newException( LbugException, "Unable to open database: " &
|
||||||
&" mismatched storage versions - file is {storageVersion}, expected {kuzuGetStorageVersion()}." )
|
&" mismatched storage versions - file is {storageVersion}, expected {lbugGetStorageVersion()}." )
|
||||||
|
|
||||||
|
|
||||||
proc newKuzuDatabase*( path="", config=kuzuConfig() ): KuzuDatabase =
|
proc newlbugDatabase*( path="", config=lbugConfig() ): LbugDatabase =
|
||||||
## Create a new Kuzu database handle. Creates an in-memory
|
## Create a new Lbug database handle. Creates an in-memory
|
||||||
## database by default, but writes to disk if a +path+ is supplied.
|
## database by default, but writes to disk if a +path+ is supplied.
|
||||||
|
|
||||||
result = new KuzuDatabase
|
result = new LbugDatabase
|
||||||
result.config = config
|
result.config = config
|
||||||
|
|
||||||
if path != "" and path != ":memory:":
|
if path != "" and path != ":memory:":
|
||||||
|
|
@ -44,13 +44,13 @@ proc newKuzuDatabase*( path="", config=kuzuConfig() ): KuzuDatabase =
|
||||||
result.path = "(in-memory)"
|
result.path = "(in-memory)"
|
||||||
result.kind = memory
|
result.kind = memory
|
||||||
|
|
||||||
result.handle = kuzu_database()
|
result.handle = lbug_database()
|
||||||
|
|
||||||
if result.kind == disk:
|
if result.kind == disk:
|
||||||
result.validateDatabase()
|
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
|
result.valid = true
|
||||||
else:
|
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 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
|
|
||||||
assert db.path == "(in-memory)"
|
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 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
# FIXME: This test should really perform some
|
# FIXME: This test should really perform some
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# vim: set et sta sw=4 ts=4 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
# There is currently no getter for this, so
|
# There is currently no getter for this, so
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# vim: set et sta sw=4 ts=4 :
|
# 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 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import re
|
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 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let version = $kuzuGetVersion()
|
let version = $lbugGetVersion()
|
||||||
assert version.contains( re"^\d+\.\d+\.\d+(?:\.\d+)?$" )
|
assert version.contains( re"^\d+\.\d+\.\d+(?:\.\d+)?$" )
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# vim: set et sta sw=4 ts=4 :
|
# 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 :
|
# 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.path == "(in-memory)"
|
||||||
assert db.kind == memory
|
assert db.kind == memory
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ import
|
||||||
std/files,
|
std/files,
|
||||||
std/paths
|
std/paths
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
const DATABASE_PATH = Path( "tmp/testdb" )
|
const DATABASE_PATH = Path( "tmp/testdb" )
|
||||||
DATABASE_PATH.removeFile()
|
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.path == "tmp/testdb"
|
||||||
assert db.config.auto_checkpoint == false
|
assert db.config.auto_checkpoint == false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import
|
||||||
std/paths,
|
std/paths,
|
||||||
std/re
|
std/re
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
const NOT_A_DATABASE_PATH = Path( "tmp/not-a-db" )
|
const NOT_A_DATABASE_PATH = Path( "tmp/not-a-db" )
|
||||||
|
|
||||||
|
|
@ -15,9 +15,9 @@ fh.write( "Hi." )
|
||||||
fh.close
|
fh.close
|
||||||
|
|
||||||
try:
|
try:
|
||||||
discard newKuzuDatabase( $NOT_A_DATABASE_PATH )
|
discard newLbugDatabase( $NOT_A_DATABASE_PATH )
|
||||||
except KuzuException as err:
|
except LbugException as err:
|
||||||
assert err.msg.contains( re"""Unable to open database: "tmp/not-a-db" Doesn't appear to be a Kuzu file""" )
|
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()
|
NOT_A_DATABASE_PATH.removeFile()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,16 @@ import
|
||||||
std/files,
|
std/files,
|
||||||
std/paths
|
std/paths
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
const DATABASE_PATH = Path( "tmp/testdb" )
|
const DATABASE_PATH = Path( "tmp/testdb" )
|
||||||
|
|
||||||
DATABASE_PATH.removeFile()
|
DATABASE_PATH.removeFile()
|
||||||
var db = newKuzuDatabase( $DATABASE_PATH )
|
var db = newLbugDatabase( $DATABASE_PATH )
|
||||||
|
|
||||||
assert db.path == $DATABASE_PATH
|
assert db.path == $DATABASE_PATH
|
||||||
assert db.kind == disk
|
assert db.kind == disk
|
||||||
assert db.config == kuzuConfig()
|
assert db.config == lbugConfig()
|
||||||
assert db.config.read_only == false
|
assert db.config.read_only == false
|
||||||
|
|
||||||
DATABASE_PATH.removeFile()
|
DATABASE_PATH.removeFile()
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# vim: set et sta sw=4 ts=4 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query """
|
var q = conn.query """
|
||||||
|
|
|
||||||
|
|
@ -4,19 +4,19 @@ discard """
|
||||||
output: "d.thing\nCamel\nLampshade\nDelicious Cake\n"
|
output: "d.thing\nCamel\nLampshade\nDelicious Cake\n"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||||
|
|
||||||
var p = conn.prepare( "CREATE (d:Doop {thing: $thing})" )
|
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" ]:
|
for thing in @[ "Camel", "Lampshade", "Delicious Cake" ]:
|
||||||
q = p.execute( (thing: thing) )
|
q = p.execute( (thing: thing) )
|
||||||
assert typeOf( q ) is KuzuQueryResult
|
assert typeOf( q ) is LbugQueryResult
|
||||||
|
|
||||||
# Fixed post v0.8.2:
|
# Fixed post v0.8.2:
|
||||||
# https://github.com/kuzudb/kuzu/issues/5102
|
# 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"
|
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
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( """CREATE NODE TABLE Doop (
|
var q = conn.query( """CREATE NODE TABLE Doop (
|
||||||
|
|
@ -21,7 +21,7 @@ var q = conn.query( """CREATE NODE TABLE Doop (
|
||||||
date DATE,
|
date DATE,
|
||||||
PRIMARY KEY(id)
|
PRIMARY KEY(id)
|
||||||
)""" )
|
)""" )
|
||||||
assert typeOf( q ) is KuzuQueryResult
|
assert typeOf( q ) is LbugQueryResult
|
||||||
|
|
||||||
|
|
||||||
var stmt = conn.prepare( """CREATE (d:Doop {
|
var stmt = conn.prepare( """CREATE (d:Doop {
|
||||||
|
|
@ -34,7 +34,7 @@ var stmt = conn.prepare( """CREATE (d:Doop {
|
||||||
uuid: UUID($uuid),
|
uuid: UUID($uuid),
|
||||||
date: DATE($date)
|
date: DATE($date)
|
||||||
})""" )
|
})""" )
|
||||||
assert typeOf( stmt ) is KuzuPreparedStatement
|
assert typeOf( stmt ) is LbugPreparedStatement
|
||||||
|
|
||||||
|
|
||||||
q = stmt.execute((
|
q = stmt.execute((
|
||||||
|
|
@ -47,7 +47,7 @@ q = stmt.execute((
|
||||||
uuid: "A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11",
|
uuid: "A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11",
|
||||||
date: "2025-03-29"
|
date: "2025-03-29"
|
||||||
))
|
))
|
||||||
assert typeOf( q ) is KuzuQueryResult
|
assert typeOf( q ) is LbugQueryResult
|
||||||
|
|
||||||
|
|
||||||
q = conn.query( "MATCH (d:Doop) RETURN d.*" )
|
q = conn.query( "MATCH (d:Doop) RETURN d.*" )
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ discard """
|
||||||
output: "a\nb\nc\nd\ne\nf\n"
|
output: "a\nb\nc\nd\ne\nf\n"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "RETURN 'hi'" )
|
var q = conn.query( "RETURN 'hi'" )
|
||||||
|
|
||||||
assert typeOf( q ) is KuzuQueryResult
|
assert typeOf( q ) is LbugQueryResult
|
||||||
assert q.sets.len == 0
|
assert q.sets.len == 0
|
||||||
|
|
||||||
q = conn.query """
|
q = conn.query """
|
||||||
|
|
@ -23,7 +23,7 @@ q = conn.query """
|
||||||
RETURN "f";
|
RETURN "f";
|
||||||
"""
|
"""
|
||||||
|
|
||||||
assert typeOf( q ) is KuzuQueryResult
|
assert typeOf( q ) is LbugQueryResult
|
||||||
assert q.sets.len == 5
|
assert q.sets.len == 5
|
||||||
|
|
||||||
echo q.getNext
|
echo q.getNext
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
# vim: set et sta sw=4 ts=4 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
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" ]:
|
for thing in @[ "Camel", "Lampshade", "Delicious Cake" ]:
|
||||||
q = conn.query( "CREATE (d:Doop {thing: '" & thing & "'})" )
|
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" )
|
q = conn.query( "MATCH (d:Doop) RETURN d.thing" )
|
||||||
assert q.num_columns == 1
|
assert q.num_columns == 1
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ discard """
|
||||||
output: "Camel\nLampshade\nDelicious Cake\n"
|
output: "Camel\nLampshade\nDelicious Cake\n"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
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"
|
output: "Camel\nLampshade\nCamel\nLampshade\n"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
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"
|
output: "d.thing\nokay!\n\n"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
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 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
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" )
|
q = conn.query( "MATCH (d:Doop) RETURN d.thing" )
|
||||||
assert q.num_tuples == 0
|
assert q.num_tuples == 0
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
# vim: set et sta sw=4 ts=4 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
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( "CREATE (d:Doop {thing: 'okay!'})" )
|
||||||
q = conn.query( "MATCH (d:Doop) RETURN d.id AS IDENTIFIER, d.thing AS THING" )
|
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 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
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( "CREATE (d:Doop {thing: 'okay!'})" )
|
||||||
q = conn.query( "MATCH (d:Doop) RETURN d.id AS IDENTIFIER, d.thing AS THING" )
|
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.len == 2
|
||||||
assert $q.column_types[0] == "KUZU_SERIAL"
|
assert $q.column_types[0] == "LBUG_SERIAL"
|
||||||
assert $q.column_types[1] == "KUZU_STRING"
|
assert $q.column_types[1] == "LBUG_STRING"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
std/re
|
std/re
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
try:
|
try:
|
||||||
discard conn.query( "NOPE NOPE NOPE" )
|
discard conn.query( "NOPE NOPE NOPE" )
|
||||||
except KuzuQueryError as err:
|
except LbugQueryError as err:
|
||||||
assert err.msg.contains( re"""Parser exception: extraneous input 'NOPE'""" )
|
assert err.msg.contains( re"""Parser exception: extraneous input 'NOPE'""" )
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,30 +2,30 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
std/re
|
std/re
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, created DATE, PRIMARY KEY(id) )" )
|
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})" )
|
var p = conn.prepare( "CREATE (d:Doop {created: $created})" )
|
||||||
assert typeOf( p ) is KuzuPreparedStatement
|
assert typeOf( p ) is LbugPreparedStatement
|
||||||
|
|
||||||
# Typecast binding failure
|
# Typecast binding failure
|
||||||
#
|
#
|
||||||
try:
|
try:
|
||||||
discard p.execute( (created: "1111-1111") )
|
discard p.execute( (created: "1111-1111") )
|
||||||
except KuzuQueryError as err:
|
except LbugQueryError as err:
|
||||||
assert err.msg.contains( re"""Expression \$created has data type STRING but expected DATE.""" )
|
assert err.msg.contains( re"""Conversion exception: Error occurred during parsing date.""" )
|
||||||
|
|
||||||
# Invalid value for typecast
|
# Invalid value for typecast
|
||||||
#
|
#
|
||||||
p = conn.prepare( "CREATE (d:Doop {created: DATE($created)})" )
|
p = conn.prepare( "CREATE (d:Doop {created: DATE($created)})" )
|
||||||
try:
|
try:
|
||||||
discard p.execute( (created: "1111-1111") )
|
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\)""" )
|
assert err.msg.contains( re"""Given: "1111-1111". Expected format: \(YYYY-MM-DD\)""" )
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,19 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
std/re
|
std/re
|
||||||
import kuzu
|
import ladybug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||||
|
|
||||||
var p = conn.prepare( "CREATE (d:Doop {thing: $thing})" )
|
var p = conn.prepare( "CREATE (d:Doop {thing: $thing})" )
|
||||||
assert typeOf( p ) is KuzuPreparedStatement
|
assert typeOf( p ) is LbugPreparedStatement
|
||||||
|
|
||||||
try:
|
try:
|
||||||
discard p.execute( (nope: "undefined var in statement!") )
|
discard p.execute( (nope: "undefined var in statement!", thing: "yep") )
|
||||||
except KuzuQueryError as err:
|
except LbugQueryError as err:
|
||||||
assert err.msg.contains( re"""Parameter nope not found.""" )
|
assert err.msg.contains( re"""Parameter nope not found.""" )
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,19 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
std/re
|
std/re
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||||
|
|
||||||
var p = conn.prepare( "CREAET (d:Doop {thing: $thing})" )
|
var p = conn.prepare( "CREAET (d:Doop {thing: $thing})" )
|
||||||
assert typeOf( p ) is KuzuPreparedStatement
|
assert typeOf( p ) is LbugPreparedStatement
|
||||||
|
|
||||||
try:
|
try:
|
||||||
discard p.execute
|
discard p.execute
|
||||||
except KuzuQueryError as err:
|
except LbugQueryError as err:
|
||||||
assert err.msg.contains( re"""Parser exception: extraneous input 'CREAET'""" )
|
assert err.msg.contains( re"""Parser exception: extraneous input 'CREAET'""" )
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ discard """
|
||||||
output: "okay!"
|
output: "okay!"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
std/re
|
std/re
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
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:
|
try:
|
||||||
discard q.getNext
|
discard q.getNext
|
||||||
except KuzuIterationError as err:
|
except LbugIterationError as err:
|
||||||
assert err.msg.contains( re"""Query iteration past end.""" )
|
assert err.msg.contains( re"""Query iteration past end.""" )
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
std/re
|
std/re
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
||||||
|
|
@ -15,6 +15,6 @@ let tup = q.getNext
|
||||||
|
|
||||||
try:
|
try:
|
||||||
echo tup[22]
|
echo tup[22]
|
||||||
except KuzuIndexError as err:
|
except LbugIndexError as err:
|
||||||
assert err.msg.contains( re"""Unable to fetch tuple value at idx 22.""" )
|
assert err.msg.contains( re"""Unable to fetch tuple value at idx 22.""" )
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ discard """
|
||||||
output: "okay!"
|
output: "okay!"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
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 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( """CREATE NODE TABLE Doop (
|
var q = conn.query( """CREATE NODE TABLE Doop (
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# vim: set et sta sw=4 ts=4 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
var db = newKuzuDatabase()
|
var db = newLbugDatabase()
|
||||||
var conn = db.connect
|
var conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doot ( id SERIAL, data BLOB, PRIMARY KEY(id) )" )
|
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 expected: seq[byte] = @[188, 189, 186, 170]
|
||||||
var val = q.getNext[0]
|
var val = q.getNext[0]
|
||||||
assert val.kind == KUZU_BLOB
|
assert val.kind == LBUG_BLOB
|
||||||
assert val.toBlob == expected
|
assert val.toBlob == expected
|
||||||
|
|
||||||
expected = @[72, 101, 108, 108, 111, 33]
|
expected = @[72, 101, 108, 108, 111, 33]
|
||||||
val = q.getNext[0]
|
val = q.getNext[0]
|
||||||
assert val.kind == KUZU_BLOB
|
assert val.kind == LBUG_BLOB
|
||||||
assert val.toBlob == expected
|
assert val.toBlob == expected
|
||||||
|
|
||||||
var str: string
|
var str: string
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,39 @@
|
||||||
# vim: set et sta sw=4 ts=4 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "RETURN [1,2,3,4,5] AS list" )
|
var q = conn.query( "RETURN [1,2,3,4,5] AS list" )
|
||||||
var list = q.getNext[0]
|
var list = q.getNext[0]
|
||||||
assert list.kind == KUZU_LIST
|
assert list.kind == LBUG_LIST
|
||||||
|
|
||||||
var items = list.toSeq
|
var items = list.toSeq
|
||||||
assert items.len == 5
|
assert items.len == 5
|
||||||
assert typeOf( items ) is seq[KuzuValue]
|
assert typeOf( items ) is seq[LbugValue]
|
||||||
|
|
||||||
for i in items:
|
for i in items:
|
||||||
assert( i.kind == KUZU_INT64 )
|
assert( i.kind == LBUG_INT64 )
|
||||||
|
|
||||||
|
|
||||||
q = conn.query( """RETURN ["woo", "hoo"] AS list""" )
|
q = conn.query( """RETURN ["woo", "hoo"] AS list""" )
|
||||||
list = q.getNext[0]
|
list = q.getNext[0]
|
||||||
assert list.kind == KUZU_LIST
|
assert list.kind == LBUG_LIST
|
||||||
|
|
||||||
items = list.toSeq
|
items = list.toSeq
|
||||||
assert items.len == 2
|
assert items.len == 2
|
||||||
assert typeOf( items ) is seq[KuzuValue]
|
assert typeOf( items ) is seq[LbugValue]
|
||||||
|
|
||||||
for i in items:
|
for i in items:
|
||||||
assert( i.kind == KUZU_STRING )
|
assert( i.kind == LBUG_STRING )
|
||||||
|
|
||||||
|
|
||||||
q = conn.query( """RETURN [] AS list""" )
|
q = conn.query( """RETURN [] AS list""" )
|
||||||
list = q.getNext[0]
|
list = q.getNext[0]
|
||||||
assert list.kind == KUZU_LIST
|
assert list.kind == LBUG_LIST
|
||||||
|
|
||||||
items = list.toList
|
items = list.toList
|
||||||
assert items.len == 0
|
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 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( """RETURN 12""" )
|
var q = conn.query( """RETURN 12""" )
|
||||||
try:
|
try:
|
||||||
discard q.getNext[0].toStruct
|
discard q.getNext[0].toStruct
|
||||||
except KuzuTypeError:
|
except LbugTypeError:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# vim: set et sta sw=4 ts=4 :
|
# vim: set et sta sw=4 ts=4 :
|
||||||
|
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
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 thing = row[1]
|
||||||
var node = row[2]
|
var node = row[2]
|
||||||
|
|
||||||
assert id.kind == KUZU_INT64
|
assert id.kind == LBUG_INT64
|
||||||
assert thing.kind == KUZU_STRING
|
assert thing.kind == LBUG_STRING
|
||||||
assert node.kind == KUZU_NODE
|
assert node.kind == LBUG_NODE
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
std/re
|
std/re
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( "CREATE NODE TABLE Doop ( id SERIAL, thing STRING, PRIMARY KEY(id) )" )
|
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 tup = q.getNext
|
||||||
var val = tup[0]
|
var val = tup[0]
|
||||||
assert val.kind == KUZU_NODE
|
assert val.kind == LBUG_NODE
|
||||||
|
|
||||||
try:
|
try:
|
||||||
discard val.toInt32
|
discard val.toInt32
|
||||||
except KuzuTypeError as err:
|
except LbugTypeError as err:
|
||||||
assert err.msg.contains( re"""Mismatched types: KUZU_NODE != {KUZU_INT32}""" )
|
assert err.msg.contains( re"""Mismatched types: LBUG_NODE != {LBUG_INT32}""" )
|
||||||
|
|
||||||
|
|
||||||
q = conn.query( "RETURN 1" )
|
q = conn.query( "RETURN 1" )
|
||||||
|
|
@ -27,7 +27,7 @@ val = q.getNext[0]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
discard val.toStruct
|
discard val.toStruct
|
||||||
except KuzuTypeError as err:
|
except LbugTypeError as err:
|
||||||
assert err.msg.contains( re"""Mismatched types: KUZU_INT.* != {KUZU_NODE, KUZU_REL,.*}""" )
|
assert err.msg.contains( re"""Mismatched types: LBUG_INT.* != {LBUG_NODE, LBUG_REL,.*}""" )
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
std/re
|
std/re
|
||||||
import kuzu
|
import lbug
|
||||||
|
|
||||||
let db = newKuzuDatabase()
|
let db = newLbugDatabase()
|
||||||
let conn = db.connect
|
let conn = db.connect
|
||||||
|
|
||||||
var q = conn.query( """RETURN {test1: 1, test2: "bewts"} AS struct""" )
|
var q = conn.query( """RETURN {test1: 1, test2: "bewts"} AS struct""" )
|
||||||
|
|
@ -15,7 +15,7 @@ assert struct.keys == @["test1", "test2"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
discard struct["nope"]
|
discard struct["nope"]
|
||||||
except KuzuIndexError as err:
|
except LbugIndexError as err:
|
||||||
assert err.msg.contains( re"""No such struct key "nope"""" )
|
assert err.msg.contains( re"""No such struct key "nope"""" )
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue