Skip to content
This repository was archived by the owner on Oct 10, 2025. It is now read-only.

Commit bf980bc

Browse files
royi-luoprrao87
andauthored
Add documentation on special behaviour for query result getNext() (#351)
* Add docs on query result getNext() behaviour * Add manual frees in C API example * Apply suggestions from code review --------- Co-authored-by: Prashanth Rao <35005448+prrao87@users.noreply.github.com>
1 parent 6847d34 commit bf980bc

File tree

3 files changed

+183
-4
lines changed

3 files changed

+183
-4
lines changed

src/content/docs/client-apis/c.mdx

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,62 @@ And then link against `<install-dest>/libkuzu.so` (or `libkuzu.dylib`/`libkuzu.l
7373

7474

7575
The static library is more complicated (as noted above, it's recommended that you use CMake to handle the details) and is not installed by default, but all static libraries will be available in the build directory.
76-
You need to define `KUZU_STATIC_DEFINE`, and link against the static kuzu library in `build/src`, as well as `antlr4_cypher`, `antlr4_runtime`, `brotlidec`, `brotlicommon`, `utf8proc`, `re2`, `serd`, `fastpfor`, `miniparquet`, `zstd`, `miniz`, `mbedtls`, `lz4` (all of which can be found in the third_party subdirectory of the CMake build directory. E.g. `build/third_party/zstd/libzstd.a`) and whichever standard library you're using.
76+
You need to define `KUZU_STATIC_DEFINE`, and link against the static Kùzu library in `build/src`, as well as `antlr4_cypher`, `antlr4_runtime`, `brotlidec`, `brotlicommon`, `utf8proc`, `re2`, `serd`, `fastpfor`, `miniparquet`, `zstd`, `miniz`, `mbedtls`, `lz4` (all of which can be found in the third_party subdirectory of the CMake build directory. E.g. `build/third_party/zstd/libzstd.a`) and whichever standard library you're using.
77+
78+
## Handling Kùzu output using `kuzu_query_result_get_next()`
79+
80+
For the examples in this section we will be using the following schema:
81+
```cypher
82+
CREATE NODE TABLE person(id INT64 PRIMARY KEY);
83+
```
84+
85+
The `kuzu_query_result_get_next()` function returns a reference to the resulting flat tuple. Additionally, to reduce resource allocation all calls to `kuzu_query_result_get_next()` reuse the same
86+
flat tuple object. This means that for a query result, each call to `kuzu_query_result_get_next()` actually overwrites the flat tuple previously returned by the previous call.
87+
88+
Thus, we recommend processing each tuple immediately before making the next call to `getNext`:
89+
90+
```c
91+
kuzu_query_result result;
92+
kuzu_connection_query(conn, "MATCH (p:person) RETURN p.*", result);
93+
while (kuzu_query_result_has_next(result)) {
94+
kuzu_flat_tuple tuple;
95+
kuzu_query_result_get_next(result, tuple);
96+
do_something(tuple);
97+
}
98+
```
99+
100+
If you wish to process the tuples later, you must explicitly make a copy of each tuple:
101+
```cpp
102+
static kuzu_value* copy_flat_tuple(kuzu_flat_tuple* tuple, uint32_t tupleLen) {
103+
kuzu_value* ret = malloc(sizeof(kuzu_value) * tupleLen);
104+
for (uint32_t i = 0; i < tupleLen; i++) {
105+
kuzu_flat_tuple_get_value(tuple, i, &ret[i]);
106+
}
107+
return ret;
108+
}
109+
110+
void mainFunction() {
111+
kuzu_query_result result;
112+
kuzu_connection_query(conn, "MATCH (p:person) RETURN p.*", &result);
113+
114+
uint64_t num_tuples = kuzu_query_result_get_num_tuples(&result);
115+
kuzu_value** tuples = (kuzu_value**)malloc(sizeof(kuzu_value*) * num_tuples);
116+
for (uint64_t i = 0; i < num_tuples; ++i) {
117+
kuzu_flat_tuple tuple;
118+
kuzu_query_result_get_next(&result, &tuple);
119+
tuples[i] = copy_flat_tuple(&tuple, kuzu_query_result_get_num_columns(&result));
120+
kuzu_flat_tuple_destroy(&tuple);
121+
}
122+
123+
for (uint64_t i = 0; i < num_tuples; ++i) {
124+
for (uint64_t j = 0; j < kuzu_query_result_get_num_columns(&result); ++j) {
125+
doSomething(tuples[i][j]);
126+
kuzu_value_destroy(&tuples[i][j]);
127+
}
128+
free(tuples[i]);
129+
}
130+
131+
free((void*)tuples);
132+
kuzu_query_result_destroy(&result);
133+
}
134+
```

src/content/docs/client-apis/cpp.mdx

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,71 @@ See the following link for the full documentation of the C++ API.
1111
href="https://kuzudb.com/api-docs/cpp/annotated.html"
1212
/>
1313

14+
## Handling Kùzu output using `getNext()`
15+
16+
For the examples in this section we will be using the following schema:
17+
```cypher
18+
CREATE NODE TABLE person(id INT64 PRIMARY KEY);
19+
```
20+
21+
The `getNext()` function in a `QueryResult` returns a reference to the resulting `FlatTuple`. Additionally, to reduce resource allocation all calls to `getNext()` reuse the same
22+
FlatTuple object. This means that for a `QueryResult`, each call to `getNext()` actually overwrites the `FlatTuple` previously returned by the previous call to `getNext()`.
23+
24+
Thus, we don't recommend using `QueryResult` like this:
25+
26+
```cpp
27+
std::unique_ptr<kuzu::main::QueryResult> result = conn.query("MATCH (p:person) RETURN p.*");
28+
std::vector<std::shared_ptr<kuzu::processor::FlatTuple>> tuples;
29+
while (result->hasNext()) {
30+
// Each call to getNext() actually returns a pointer to the same tuple object
31+
tuples.emplace_back(result->getNext());
32+
}
33+
34+
// This is wrong!
35+
// The vector stores a bunch of pointers to the same underlying tuple object
36+
for (const auto& resultTuple: tuples) {
37+
doSomething(resultTuple);
38+
}
39+
```
40+
41+
Instead, we recommend processing each tuple immediately before making the next call to `getNext`:
42+
```cpp
43+
std::unique_ptr<kuzu::main::QueryResult> result = conn.query("MATCH (p:person) RETURN p.*");
44+
std::vector<std::shared_ptr<kuzu::processor::FlatTuple>> tuples;
45+
while (result->hasNext()) {
46+
auto tuple = result->getNext();
47+
doSomething(tuple);
48+
}
49+
```
50+
51+
If wish to process the tuples later, you must explicitly make a copy of each tuple:
52+
```cpp
53+
static decltype(auto) copyFlatTuple(kuzu::processor::FlatTuple* tuple) {
54+
std::vector<std::unique_ptr<kuzu::common::Value>> ret;
55+
for (uint32_t i = 0; i < tuple->len(); i++) {
56+
ret.emplace_back(tuple->getValue(i)->copy());
57+
}
58+
return ret;
59+
}
60+
61+
void mainFunction() {
62+
std::unique_ptr<kuzu::main::QueryResult> result = conn->query("MATCH (p:person) RETURN p.*");
63+
std::vector<std::vector<std::unique_ptr<kuzu::common::Value>>> tuples;
64+
while (result->hasNext()) {
65+
auto tuple = result->getNext();
66+
tuples.emplace_back(copyFlatTuple(tuple.get()));
67+
}
68+
for (const auto& tuple : tuples) {
69+
doSomething(tuple);
70+
}
71+
}
72+
```
73+
74+
## UDF API
75+
1476
In addition to interfacing with the database, the C++ API offers users the ability to define custom
1577
functions via User Defined Functions (UDFs), described below.
1678
17-
## UDF API
1879
Kùzu provides two interfaces that enable you to define your own custom scalar and vectorized functions.
1980
2081
### Scalar functions
@@ -211,7 +272,7 @@ conn->createVectorizedFunction<int64_t, int64_t>("addFour", &addFour);
211272
conn->query("MATCH (p:person) return addFour(p.age)");
212273
```
213274

214-
#### Option 2. Vectorized function with input and return type in Cypher
275+
#### Option 2. Vectorized function with input and return type in Cypher
215276

216277
Create a vectorized function with input and return type in Cypher.
217278
```cpp
@@ -263,4 +324,4 @@ conn->query("MATCH (p:person) return addDate(p.birthdate, p.age)");
263324
264325
## Linking
265326
266-
See the [C API Documentation](/client-apis/c#linking) for details as linking to the C++ API is more or less identical.
327+
See the [C API Documentation](/client-apis/c#linking) for details as linking to the C++ API is more or less identical.

src/content/docs/client-apis/java.mdx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,63 @@ See the following link for the full documentation of the Java API.
1010
title="Java API documentation"
1111
href="https://kuzudb.com/api-docs/java"
1212
/>
13+
14+
## Handling Kùzu output using `getNext()`
15+
16+
For the examples in this section we will be using the following schema:
17+
```cypher
18+
CREATE NODE TABLE person(id INT64 PRIMARY KEY);
19+
```
20+
21+
The `getNext()` function in a `QueryResult` returns a reference to the resulting `FlatTuple`. Additionally, to reduce resource allocation all calls to `getNext()` reuse the same
22+
FlatTuple object. This means that for a `QueryResult`, each call to `getNext()` actually overwrites the `FlatTuple` previously returned by the previous call to `getNext()`.
23+
24+
Thus, we don't recommend using `QueryResult` like this:
25+
26+
```java
27+
QueryResult result = conn.query("MATCH (p:person) RETURN p.*");
28+
List<FlatTuple> tuples = new ArrayList<FlatTuple>();
29+
while (result.hasNext()) {
30+
// Each call to getNext() actually returns a reference to the same tuple object
31+
tuples.add(result.getNext());
32+
}
33+
34+
// This is wrong!
35+
// The list stores a bunch of references to the same underlying tuple object
36+
for (FlatTuple resultTuple: tuples) {
37+
doSomething(resultTuple);
38+
}
39+
```
40+
41+
Instead, we recommend processing each tuple immediately before making the next call to `getNext`:
42+
```java
43+
QueryResult result = conn.query("MATCH (p:person) RETURN p.*");
44+
while (result.hasNext()) {
45+
FlatTuple tuple = result.getNext();
46+
doSomething(tuple);
47+
}
48+
```
49+
50+
If wish to process the tuples later, you must explicitly make a copy of each tuple:
51+
```java
52+
List<Value> copyFlatTuple(FlatTuple tuple, long tupleLen) throws ObjectRefDestroyedException {
53+
List<Value> ret = new ArrayList<Value>();
54+
for (int i = 0; i < tupleLen; i++) {
55+
ret.add(tuple.getValue(i).clone());
56+
}
57+
return ret;
58+
}
59+
60+
void mainFunction() throws ObjectRefDestroyedException {
61+
QueryResult result = conn.query("MATCH (p:person) RETURN p.*");
62+
List<List<Value>> tuples = new ArrayList<List<Value>>();
63+
while (result.hasNext()) {
64+
FlatTuple tuple = result.getNext();
65+
tuples.add(copyFlatTuple(tuple, result.getNumColumns()));
66+
}
67+
68+
for (List<Value> tuple: tuples) {
69+
doSomething(tuple);
70+
}
71+
}
72+
```

0 commit comments

Comments
 (0)