Skip to content

Commit e5eeafb

Browse files
committed
Added edge cases to Python Arrow bindings
These edge cases are to handle when the buffers returned by a dataset read are empty. Specifically, an array's offsets must still contain the initial 0 value even when there's no data, and a string array must always have 3 buffers, even when there's no data.
1 parent 8bbe510 commit e5eeafb

1 file changed

Lines changed: 25 additions & 17 deletions

File tree

apis/python/src/tiledbvcf/binding/vcf_arrow.cc

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -141,32 +141,32 @@ static std::pair<ArrowSchema*, ArrowArray*> arrow_string_array(
141141
schema->release = &release_schema;
142142
schema->private_data = nullptr;
143143

144-
int n_buffers = offsets.capacity() ? 3 : 2;
145-
146144
auto array = (struct ArrowArray*)malloc(sizeof(struct ArrowArray));
147145
array->length = length;
148146
array->null_count = 0;
149147
array->offset = 0;
150-
array->n_buffers = n_buffers;
148+
array->n_buffers = 3;
151149
array->n_children = 0;
152150
array->buffers = nullptr;
153151
array->children = nullptr;
154152
array->dictionary = nullptr;
155153
array->release = &release_array;
156154
array->private_data = (void*)new ArrowBuffer(buffer);
157155

158-
array->buffers = (const void**)malloc(n_buffers * sizeof(void*));
159-
array->buffers[0] = nullptr; // validity
160-
array->buffers[n_buffers - 1] = data.data(); // data
161-
if (n_buffers == 3) {
162-
array->buffers[1] = offsets.data(); // offsets
163-
}
164-
156+
array->buffers = (const void**)malloc(3 * sizeof(void*));
165157
if (bitmap.capacity()) {
166158
schema->flags |= ARROW_FLAG_NULLABLE;
167159
array->null_count = -1;
168160
array->buffers[0] = bitmap.data();
161+
} else {
162+
array->buffers[0] = nullptr; // validity
163+
}
164+
// Edge case: empty results should still have a single 0 offset
165+
if (!length && !offsets.size()) {
166+
offsets.push_back(0);
169167
}
168+
array->buffers[1] = offsets.data(); // offsets
169+
array->buffers[2] = data.data(); // data
170170

171171
return std::make_pair(schema, array);
172172
}
@@ -205,7 +205,11 @@ static std::pair<ArrowSchema*, ArrowArray*> arrow_list_array(
205205
array->private_data = nullptr; // Buffers will be deleted by the child array
206206

207207
array->buffers = (const void**)malloc(array->n_buffers * sizeof(void*));
208-
array->buffers[0] = nullptr; // validity
208+
array->buffers[0] = nullptr; // validity
209+
// Edge case: empty results should still have a single 0 offset
210+
if (!length && !value_offsets.size()) {
211+
value_offsets.push_back(0);
212+
}
209213
array->buffers[1] = value_offsets.data(); // data
210214

211215
if (bitmap.capacity()) {
@@ -234,12 +238,12 @@ void build_arrow_array(
234238
auto [values_schema, values_array] =
235239
arrow_data_array(buffer, num_data_elements, buffer->data());
236240

241+
// Edge case: only subtract if there's offsets to avoid underflow
242+
if (num_offsets) {
243+
num_offsets -= 1;
244+
}
237245
std::tie(array_schema, array_array) = arrow_list_array(
238-
buffer,
239-
num_offsets - 1,
240-
buffer->offsets(),
241-
values_schema,
242-
values_array);
246+
buffer, num_offsets, buffer->offsets(), values_schema, values_array);
243247
} else {
244248
std::tie(array_schema, array_array) =
245249
arrow_data_array(buffer, num_data_elements, buffer->data());
@@ -278,9 +282,13 @@ void build_arrow_array_from_buffer(
278282
uint64_t num_data_elements) {
279283
if (buffer->datatype() == TILEDB_VCF_CHAR) {
280284
if (buffer->list_offsets().capacity() > 0) {
285+
// Edge case: only subtract if there's offsets to avoid underflow
286+
if (num_offsets) {
287+
num_offsets -= 1;
288+
}
281289
// Array of strings
282290
auto [values_schema, values_array] = arrow_string_array(
283-
buffer, num_offsets - 1, buffer->offsets(), buffer->data());
291+
buffer, num_offsets, buffer->offsets(), buffer->data());
284292

285293
// Array of lists of strings
286294
auto [arrow_schema, arrow_array] = arrow_list_array(

0 commit comments

Comments
 (0)