diff --git a/src/kuzu/queries.nim b/src/kuzu/queries.nim index 76d32a3..03fe293 100644 --- a/src/kuzu/queries.nim +++ b/src/kuzu/queries.nim @@ -147,14 +147,14 @@ func hasNext*( query: KuzuQueryResult ): bool = func getNext*( query: KuzuQueryResult ): KuzuFlatTuple = - ## Consume and return the next tuple result, or raise a KuzuIndexError - ## if at the end of the result set. + ## 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( KuzuIndexError, &"Query iteration past end." ) + raise newException( KuzuIterationError, &"Query iteration past end of tuples." ) func rewind*( query: KuzuQueryResult ) = @@ -162,6 +162,32 @@ func rewind*( query: KuzuQueryResult ) = kuzu_query_result_reset_iterator( addr query.handle ) +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 ) + + +# Keeping this private, because it's only safe to call from within +# an iterator -- overwriting a query variable with the next result +# goes boom. +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." ) + + +iterator sets*( query: KuzuQueryResult ): KuzuQueryResult = + ## Iterate available query result sets, yielding to the block. + while query.hasNextSet: + yield query.getNextSet + # NOTE: There is no 'rewind' mechanism for result sets. + + iterator items*( query: KuzuQueryResult ): KuzuFlatTuple = ## Iterate available tuples, yielding to the block. while query.hasNext: diff --git a/src/kuzu/types.nim b/src/kuzu/types.nim index 59e5c5a..f5026eb 100644 --- a/src/kuzu/types.nim +++ b/src/kuzu/types.nim @@ -48,8 +48,9 @@ type keys*: seq[ string ] KuzuStructValue* = ref KuzuStructValueObj - KuzuException* = object of CatchableError - KuzuQueryError* = object of KuzuException - KuzuIndexError* = object of KuzuException - KuzuTypeError* = object of KuzuException + KuzuException* = object of CatchableError + KuzuQueryError* = object of KuzuException + KuzuIndexError* = object of KuzuException + KuzuIterationError* = object of KuzuException + KuzuTypeError* = object of KuzuException diff --git a/tests/queries/t_can_contain_multiple_result_sets.nim b/tests/queries/t_can_contain_multiple_result_sets.nim new file mode 100644 index 0000000..d0be2d5 --- /dev/null +++ b/tests/queries/t_can_contain_multiple_result_sets.nim @@ -0,0 +1,42 @@ +# vim: set et sta sw=4 ts=4 : + +discard """ +output: "Jenny|Lenny\nLenny\nJenny\n" +""" + +import kuzu + +let db = newKuzuDatabase() +let conn = db.connect + +var q = conn.query """ +CREATE NODE TABLE User( + id SERIAL PRIMARY KEY, + name STRING +); + +CREATE REL TABLE FOLLOWS( + From User To User +); + +MERGE (a:User {name: "Lenny"})-[f:Follows]->(b:User {name: "Jenny"}); +""" + +q = conn.query( "MATCH (u:User) RETURN *" ) + +assert typeOf( q ) is KuzuQueryResult +assert q.hasNextSet == false + +q = conn.query """ + MATCH (a:User)<-[f:Follows]-(b:User) RETURN a.name, b.name; + MATCH (u:User) RETURN u.name; +""" + +assert typeOf( q ) is KuzuQueryResult +assert q.hasNextSet == true + +echo q.getNext +for query_result in q.sets: + for row in query_result.items: + echo row +