diff --git a/.gitignore b/.gitignore index 33b6351..c3fcff2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,13 @@ experiments/imdb/* +!experiments/imdb/*.nim +!experiments/imdb/Makefile nimble.paths config.nims tmp/* kuzu nimcache/* tests/* +!tests/*/t_*.nim testresults/* testresults.html diff --git a/USAGE.md b/USAGE.md index 17f5087..23f7682 100644 --- a/USAGE.md +++ b/USAGE.md @@ -358,8 +358,10 @@ Manually rewind the `KuzuQueryResult` via `rewind()`. ## Multiple Query Results -A query can potentially return any number of separate statements. Iterate over -linked `KuzuQueryResult` objects with the `sets()` iterator. +A query can potentially return any number of separate statements. In the case +of more potential `RETURN`s, the query will only contain the first. Iterate +over linked `KuzuQueryResult` objects with the `sets()` iterator to retreive the +remaining: ```nim import kuzu diff --git a/experiments/imdb/imdb-results.pdf b/experiments/imdb/imdb-results.pdf index 5a092e5..a429af5 100644 Binary files a/experiments/imdb/imdb-results.pdf and b/experiments/imdb/imdb-results.pdf differ diff --git a/kuzu.nimble b/kuzu.nimble index b124df0..81770d8 100644 --- a/kuzu.nimble +++ b/kuzu.nimble @@ -18,9 +18,7 @@ task makewrapper, "Generate the C wrapper using Futhark": task test, "Run the test suite.": exec "testament --megatest:off all" exec "testament html" - -task clean, "Remove all non-repository artifacts.": - exec "fossil clean -x" + exec """find tests/ -type f \! -name \*.nim -delete""" task docs, "Generate automated documentation.": exec "nim md2html --project --outdir:docs README.md" diff --git a/src/kuzu.nim b/src/kuzu.nim index cf52a41..36ff91b 100644 --- a/src/kuzu.nim +++ b/src/kuzu.nim @@ -13,6 +13,8 @@ else: include "kuzu/0.11.0.nim" import + std/files, + std/paths, std/strformat, std/strutils diff --git a/src/kuzu/constants.nim b/src/kuzu/constants.nim index 705937e..7cb6a82 100644 --- a/src/kuzu/constants.nim +++ b/src/kuzu/constants.nim @@ -4,5 +4,5 @@ const KUZU_VERSION* = "0.6.0" const KUZU_EXPECTED_LIBVERSION* = "0.11.0" const BLOB_MAXSIZE = 4096 -let KUZU_DEFAULT_CONFIG* = kuzu_default_system_config() +let KUZU_DEFAULT_CONFIG* = kuzu_default_system_config() diff --git a/src/kuzu/database.nim b/src/kuzu/database.nim index 830f552..20c1e84 100644 --- a/src/kuzu/database.nim +++ b/src/kuzu/database.nim @@ -7,18 +7,50 @@ proc `=destroy`*( db: KuzuDatabaseObj ) = kuzu_database_destroy( addr db.handle ) -func newKuzuDatabase*( path="", config=kuzuConfig() ): KuzuDatabase = +proc validateDatabase( db: KuzuDatabase ): void = + ## Perform basic validity checks against an existing on disk database + ## for better error messaging. + + if not Path( db.path ).fileExists: return + + var buf = newSeq[char]( 5 ) + let f = open( db.path ) + discard f.readChars( buf ) + f.close + + let magic = buf[0..3].join + let storage_version = buf[4].uint + + if magic != "KUZU": + raise newException( KuzuException, "Unable to open database: " & + &""""{db.path}" Doesn't appear to be a Kuzu file.""" ) + + if storageVersion != kuzuGetStorageVersion(): + raise newException( KuzuException, "Unable to open database: " & + &" mismatched storage versions - file is {storageVersion}, expected {kuzuGetStorageVersion()}." ) + + +proc newKuzuDatabase*( path="", config=kuzuConfig() ): KuzuDatabase = ## Create a new Kuzu database handle. Creates an in-memory ## database by default, but writes to disk if a +path+ is supplied. result = new KuzuDatabase result.config = config - result.path = if path != "" and path != ":memory:": path else: "(in-memory)" + + if path != "" and path != ":memory:": + result.path = path + result.kind = disk + else: + result.path = "(in-memory)" + result.kind = memory + result.handle = kuzu_database() + if result.kind == disk: + result.validateDatabase() + if kuzu_database_init( path, config, addr result.handle ) == KuzuSuccess: result.valid = true else: raise newException( KuzuException, "Unable to open database." ) - diff --git a/src/kuzu/types.nim b/src/kuzu/types.nim index dc2dbb9..24feb78 100644 --- a/src/kuzu/types.nim +++ b/src/kuzu/types.nim @@ -1,9 +1,13 @@ # 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 diff --git a/tests/database/t_can_create_in_memory.nim b/tests/database/t_can_create_in_memory.nim index ca96333..08333c3 100644 --- a/tests/database/t_can_create_in_memory.nim +++ b/tests/database/t_can_create_in_memory.nim @@ -4,4 +4,5 @@ import kuzu var db = newKuzuDatabase() assert db.path == "(in-memory)" +assert db.kind == memory diff --git a/tests/database/t_checks_for_valid_kuzu_file.nim b/tests/database/t_checks_for_valid_kuzu_file.nim new file mode 100644 index 0000000..dd7eb95 --- /dev/null +++ b/tests/database/t_checks_for_valid_kuzu_file.nim @@ -0,0 +1,23 @@ +# vim: set et sta sw=4 ts=4 : + +import + std/files, + std/paths, + std/re + +import kuzu + +const NOT_A_DATABASE_PATH = Path( "tmp/not-a-db" ) + +NOT_A_DATABASE_PATH.removeFile() +var fh = NOT_A_DATABASE_PATH.string.open( fmWrite ) +fh.write( "Hi." ) +fh.close + +try: + discard newKuzuDatabase( $NOT_A_DATABASE_PATH ) +except KuzuException as err: + assert err.msg.contains( re"""Unable to open database: "tmp/not-a-db" Doesn't appear to be a Kuzu file""" ) + +NOT_A_DATABASE_PATH.removeFile() + diff --git a/tests/database/t_creates_with_default_config.nim b/tests/database/t_creates_with_default_config.nim index 88c3fd4..3e8cafb 100644 --- a/tests/database/t_creates_with_default_config.nim +++ b/tests/database/t_creates_with_default_config.nim @@ -12,6 +12,7 @@ DATABASE_PATH.removeFile() var db = newKuzuDatabase( $DATABASE_PATH ) assert db.path == $DATABASE_PATH +assert db.kind == disk assert db.config == kuzuConfig() assert db.config.read_only == false