Skip to content

Commit ab90339

Browse files
committed
basic read-write test
1 parent 7f878cd commit ab90339

5 files changed

Lines changed: 204 additions & 52 deletions

File tree

vortex-ffi/cbindgen.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ style = "type"
88
header = """
99
// SPDX-License-Identifier: Apache-2.0
1010
// SPDX-FileCopyrightText: Copyright the Vortex contributors
11+
#include <stdint.h>
1112
1213
//
1314
// THIS FILE IS AUTO-GENERATED, DO NOT MAKE EDITS DIRECTLY

vortex-ffi/cinclude/vortex.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
#include <stdint.h>
34

45
//
56
// THIS FILE IS AUTO-GENERATED, DO NOT MAKE EDITS DIRECTLY
@@ -1345,6 +1346,7 @@ void vx_struct_column_builder_free(vx_struct_column_builder *ptr);
13451346
/**
13461347
* Create a new column-wise struct array builder with given validity and a
13471348
* capacity hint. validity can't be NULL.
1349+
* Capacity hint is for the number of columns.
13481350
* If you don't know capacity, pass 0.
13491351
* if validity is NULL, returns NULL.
13501352
*/

vortex-ffi/src/array.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ pub unsafe extern "C-unwind" fn vx_array_dtype(array: *const vx_array) -> *const
192192
vx_dtype::new_ref(vx_array::as_ref(array).dtype())
193193
}
194194

195+
// Return an owned field for array at index.
196+
// Returns NULL and sets error_out if index is out of bounds or array doesn't
197+
// have dtype DTYPE_STRUCT.
195198
#[unsafe(no_mangle)]
196199
pub unsafe extern "C-unwind" fn vx_array_get_field(
197200
array: *const vx_array,

vortex-ffi/src/struct_array.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ crate::box_wrapper!(StructBuilder, vx_struct_column_builder);
2828

2929
/// Create a new column-wise struct array builder with given validity and a
3030
/// capacity hint. validity can't be NULL.
31+
/// Capacity hint is for the number of columns.
3132
/// If you don't know capacity, pass 0.
3233
/// if validity is NULL, returns NULL.
3334
#[unsafe(no_mangle)]

vortex-ffi/test/scan.cpp

Lines changed: 197 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,78 @@ struct TempPath : fs::path {
1818
}
1919
};
2020

21-
constexpr size_t SAMPLE_ROWS = 0;
22-
[[nodiscard]] TempPath write_sample(vx_session *session, fs::path &&path) {
23-
REQUIRE(path.is_absolute());
21+
// StructArray { age: u8, height: u16? }
22+
[[nodiscard]] const vx_dtype *sample_dtype() {
2423
vx_struct_fields_builder *builder = vx_struct_fields_builder_new();
2524

26-
{
27-
constexpr auto col1 = "col1"sv;
28-
const vx_string *col1_name = vx_string_new(col1.data(), col1.size());
29-
const vx_dtype *col1_dtype = vx_dtype_new_primitive(PTYPE_U8, false);
30-
vx_struct_fields_builder_add_field(builder, col1_name, col1_dtype);
25+
constexpr auto age = "age"sv;
26+
const vx_string *age_name = vx_string_new(age.data(), age.size());
27+
const vx_dtype *age_type = vx_dtype_new_primitive(PTYPE_U8, false);
28+
vx_struct_fields_builder_add_field(builder, age_name, age_type);
29+
30+
constexpr auto height = "height"sv;
31+
const vx_string *height_name = vx_string_new(height.data(), height.size());
32+
const vx_dtype *height_type = vx_dtype_new_primitive(PTYPE_U16, true);
33+
vx_struct_fields_builder_add_field(builder, height_name, height_type);
34+
35+
vx_struct_fields *fields = vx_struct_fields_builder_finalize(builder);
36+
return vx_dtype_new_struct(fields, false);
37+
}
38+
39+
constexpr size_t SAMPLE_ROWS = 100;
40+
[[nodiscard]] const vx_array *sample_array() {
41+
vx_validity validity = {};
42+
validity.type = VX_VALIDITY_NON_NULLABLE;
43+
44+
vx_struct_column_builder *builder = vx_struct_column_builder_new(&validity, SAMPLE_ROWS);
45+
46+
vx_error *error = nullptr;
47+
48+
std::vector<uint8_t> age_buffer;
49+
for (uint8_t age = 0; age < SAMPLE_ROWS; ++age) {
50+
age_buffer.push_back(age);
3151
}
32-
{
33-
constexpr auto col2 = "col2"sv;
34-
const vx_string *col2_name = vx_string_new(col2.data(), col2.size());
35-
const vx_dtype *col2_dtype = vx_dtype_new_utf8(true);
36-
vx_struct_fields_builder_add_field(builder, col2_name, col2_dtype);
52+
const vx_array *age_array =
53+
vx_array_new_primitive(PTYPE_U8, age_buffer.data(), age_buffer.size(), &validity, &error);
54+
require_no_error(error);
55+
56+
vx_struct_column_builder_add_field(builder, "age", age_array, &error);
57+
require_no_error(error);
58+
vx_array_free(age_array);
59+
60+
std::vector<uint16_t> height_buffer;
61+
for (uint16_t height = 0; height < SAMPLE_ROWS; ++height) {
62+
height_buffer.push_back(1 + rand() % (height + 1));
3763
}
64+
validity.type = VX_VALIDITY_ALL_VALID;
65+
const vx_array *height_array =
66+
vx_array_new_primitive(PTYPE_U16, height_buffer.data(), height_buffer.size(), &validity, &error);
67+
require_no_error(error);
3868

39-
vx_struct_fields *fields = vx_struct_fields_builder_finalize(builder);
40-
const vx_dtype *file_dtype = vx_dtype_new_struct(fields, false);
69+
vx_struct_column_builder_add_field(builder, "height", height_array, &error);
70+
require_no_error(error);
71+
vx_array_free(height_array);
72+
73+
const vx_array *array = vx_struct_column_builder_finalize(builder, &error);
74+
require_no_error(error);
75+
return array;
76+
}
77+
78+
[[nodiscard]] TempPath write_sample(vx_session *session, fs::path &&path) {
79+
REQUIRE(path.is_absolute());
80+
81+
const vx_dtype *dtype = sample_dtype();
4182

4283
vx_error *error = nullptr;
43-
vx_array_sink *sink = vx_array_sink_open_file(session, path.c_str(), file_dtype, &error);
84+
vx_array_sink *sink = vx_array_sink_open_file(session, path.c_str(), dtype, &error);
4485
REQUIRE(sink != nullptr);
4586
require_no_error(error);
46-
vx_dtype_free(file_dtype);
87+
vx_dtype_free(dtype);
4788

48-
for (size_t i = 0; i < SAMPLE_ROWS; ++i) {
49-
//const vx_array* array = vx_array_new_primitive();
50-
//vx_array_sink_push(sink, array, &error);
51-
//require_no_error(error);
52-
//vx_array_free(array);
53-
}
89+
const vx_array *array = sample_array();
90+
vx_array_sink_push(sink, array, &error);
91+
require_no_error(error);
92+
vx_array_free(array);
5493

5594
vx_array_sink_close(sink, &error);
5695
require_no_error(error);
@@ -110,6 +149,12 @@ TEST_CASE("Creating datasources", "[datasource]") {
110149
vx_session_free(session);
111150
}
112151

152+
TEST_CASE("Write file", "[datasource]") {
153+
vx_session *session = vx_session_new();
154+
TempPath path = write_sample(session, fs::current_path() / "write-file.vortex");
155+
vx_session_free(session);
156+
}
157+
113158
TEST_CASE("Write file and read back types", "[datasource]") {
114159
vx_session *session = vx_session_new();
115160
TempPath path = write_sample(session, fs::current_path() / "write-read-types.vortex");
@@ -142,43 +187,143 @@ TEST_CASE("Write file and read back types", "[datasource]") {
142187
REQUIRE(vx_dtype_get_variant(col1_dtype) == DTYPE_PRIMITIVE);
143188
REQUIRE(vx_dtype_primitive_ptype(col1_dtype) == PTYPE_U8);
144189
REQUIRE_FALSE(vx_dtype_is_nullable(col1_dtype));
145-
REQUIRE(to_string_view(col1_name) == "col1");
190+
REQUIRE(to_string_view(col1_name) == "age");
146191
vx_dtype_free(col1_dtype);
147192

148193
const vx_dtype *col2_dtype = vx_struct_fields_field_dtype(fields, 1);
149194
const vx_string *col2_name = vx_struct_fields_field_name(fields, 1);
150195

151-
REQUIRE(vx_dtype_get_variant(col2_dtype) == DTYPE_UTF8);
196+
REQUIRE(vx_dtype_get_variant(col2_dtype) == DTYPE_PRIMITIVE);
197+
REQUIRE(vx_dtype_primitive_ptype(col2_dtype) == PTYPE_U16);
152198
REQUIRE(vx_dtype_is_nullable(col2_dtype));
153-
REQUIRE(to_string_view(col2_name) == "col2");
199+
REQUIRE(to_string_view(col2_name) == "height");
154200
vx_dtype_free(col2_dtype);
155201

156202
vx_data_source_free(ds);
157203
}
158204

159-
//TEST_CASE("Write file and read back", "[datasource]") {
160-
// vx_session *session = vx_session_new();
161-
// TempPath path = write_sample(session, fs::current_path() / "write-read.vortex");
162-
// vx_error *error = nullptr;
163-
//
164-
// vx_data_source_options ds_options = {};
165-
// ds_options.files = path.c_str();
166-
//
167-
// const vx_data_source *ds = vx_data_source_new(session, &ds_options, &error);
168-
// require_no_error(error);
169-
// REQUIRE(ds != nullptr);
170-
//
171-
// vx_scan *scan = vx_data_source_scan(ds, nullptr, nullptr, &error);
172-
// require_no_error(error);
173-
// REQUIRE(scan != nullptr);
174-
// vx_scan_free(scan);
175-
//
176-
// vx_scan_options scan_options = {};
177-
// scan = vx_data_source_scan(ds, &scan_options, nullptr, &error);
178-
// require_no_error(error);
179-
// REQUIRE(scan != nullptr);
180-
// vx_scan_free(scan);
181-
//
182-
// vx_data_source_free(ds);
183-
// vx_session_free(session);
184-
//}
205+
void verify_sample_array(const vx_array *array) {
206+
REQUIRE(vx_array_len(array) == SAMPLE_ROWS);
207+
REQUIRE(vx_array_has_dtype(array, DTYPE_STRUCT));
208+
209+
const vx_struct_fields *fields = vx_dtype_struct_dtype(vx_array_dtype(array));
210+
size_t len = vx_struct_fields_nfields(fields);
211+
REQUIRE(len == 2);
212+
213+
const vx_dtype *age_dtype = vx_struct_fields_field_dtype(fields, 0);
214+
REQUIRE(vx_dtype_get_variant(age_dtype) == DTYPE_PRIMITIVE);
215+
REQUIRE(vx_dtype_primitive_ptype(age_dtype) == PTYPE_U8);
216+
vx_dtype_free(age_dtype);
217+
const vx_string *age_name = vx_struct_fields_field_name(fields, 0);
218+
REQUIRE(to_string_view(age_name) == "age");
219+
220+
const vx_dtype *height_dtype = vx_struct_fields_field_dtype(fields, 1);
221+
REQUIRE(vx_dtype_get_variant(height_dtype) == DTYPE_PRIMITIVE);
222+
REQUIRE(vx_dtype_primitive_ptype(height_dtype) == PTYPE_U16);
223+
vx_dtype_free(height_dtype);
224+
const vx_string *height_name = vx_struct_fields_field_name(fields, 1);
225+
REQUIRE(to_string_view(height_name) == "height");
226+
227+
vx_error *error = nullptr;
228+
vx_validity validity = {};
229+
vx_array_get_validity(array, &validity, &error);
230+
require_no_error(error);
231+
REQUIRE(validity.type == VX_VALIDITY_NON_NULLABLE);
232+
233+
const vx_array *age_field = vx_array_get_field(array, 0, &error);
234+
require_no_error(error);
235+
REQUIRE(vx_array_has_dtype(age_field, DTYPE_PRIMITIVE));
236+
REQUIRE(vx_dtype_primitive_ptype(vx_array_dtype(age_field)) == PTYPE_U8);
237+
REQUIRE(vx_array_len(age_field) == SAMPLE_ROWS);
238+
for (size_t i = 0; i < SAMPLE_ROWS; ++i) {
239+
REQUIRE(vx_array_get_u8(age_field, i) == i);
240+
}
241+
vx_array_free(age_field);
242+
243+
const vx_array *height_field = vx_array_get_field(array, 1, &error);
244+
require_no_error(error);
245+
REQUIRE(vx_array_has_dtype(height_field, DTYPE_PRIMITIVE));
246+
REQUIRE(vx_dtype_primitive_ptype(vx_array_dtype(height_field)) == PTYPE_U16);
247+
REQUIRE(vx_array_len(height_field) == SAMPLE_ROWS);
248+
for (size_t i = 0; i < SAMPLE_ROWS; ++i) {
249+
REQUIRE(vx_array_get_u16(height_field, i) > 0);
250+
}
251+
vx_array_free(height_field);
252+
253+
REQUIRE(vx_array_get_field(array, 2, &error) == nullptr);
254+
REQUIRE(error != nullptr);
255+
vx_error_free(error);
256+
}
257+
258+
TEST_CASE("Requesting scans", "[datasource]") {
259+
vx_session *session = vx_session_new();
260+
TempPath path = write_sample(session, fs::current_path() / "write-file2.vortex");
261+
262+
vx_data_source_options ds_options = {};
263+
ds_options.files = path.c_str();
264+
265+
vx_error *error = nullptr;
266+
const vx_data_source *ds = vx_data_source_new(session, &ds_options, &error);
267+
require_no_error(error);
268+
REQUIRE(ds != nullptr);
269+
270+
vx_scan *scan = vx_data_source_scan(ds, nullptr, nullptr, &error);
271+
require_no_error(error);
272+
REQUIRE(scan != nullptr);
273+
vx_scan_free(scan);
274+
275+
vx_scan_options options = {};
276+
options.max_threads = 1;
277+
scan = vx_data_source_scan(ds, &options, nullptr, &error);
278+
require_no_error(error);
279+
REQUIRE(scan != nullptr);
280+
vx_scan_free(scan);
281+
282+
vx_data_source_free(ds);
283+
vx_session_free(session);
284+
}
285+
286+
TEST_CASE("Basic scan", "[datasource]") {
287+
vx_session *session = vx_session_new();
288+
TempPath path = write_sample(session, fs::current_path() / "basic-scan.vortex");
289+
vx_error *error = nullptr;
290+
291+
vx_data_source_options ds_options = {};
292+
ds_options.files = path.c_str();
293+
294+
const vx_data_source *ds = vx_data_source_new(session, &ds_options, &error);
295+
require_no_error(error);
296+
REQUIRE(ds != nullptr);
297+
298+
vx_scan *scan = vx_data_source_scan(ds, nullptr, nullptr, &error);
299+
require_no_error(error);
300+
REQUIRE(scan != nullptr);
301+
302+
vx_partition *partition = vx_scan_next(scan, &error);
303+
require_no_error(error);
304+
305+
vx_estimate estimate = {};
306+
vx_partition_row_count(partition, &estimate, &error);
307+
require_no_error(error);
308+
REQUIRE(estimate.type == VX_ESTIMATE_EXACT);
309+
REQUIRE(estimate.estimate == SAMPLE_ROWS);
310+
311+
REQUIRE(vx_scan_next(scan, &error) == nullptr);
312+
require_no_error(error);
313+
314+
const vx_array *array = vx_partition_next(partition, &error);
315+
require_no_error(error);
316+
REQUIRE(array != nullptr);
317+
318+
REQUIRE(vx_partition_next(partition, &error) == nullptr);
319+
require_no_error(error);
320+
321+
verify_sample_array(array);
322+
323+
vx_array_free(array);
324+
vx_partition_free(partition);
325+
vx_scan_free(scan);
326+
327+
vx_data_source_free(ds);
328+
vx_session_free(session);
329+
}

0 commit comments

Comments
 (0)