Skip to content

Commit 4ab482d

Browse files
committed
better support for union, list and map children
1 parent d9c746e commit 4ab482d

File tree

2 files changed

+141
-60
lines changed

2 files changed

+141
-60
lines changed

c_src/adbc_nif.cpp

Lines changed: 124 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ template <typename M> static ERL_NIF_TERM strings_from_buffer(
9696
return enif_make_list_from_array(env, values.data(), (unsigned)values.size());
9797
}
9898

99-
static ERL_NIF_TERM arrow_array_to_nif_term(ErlNifEnv *env, struct ArrowSchema * schema, struct ArrowArray * values, uint64_t level);
99+
static int arrow_array_to_nif_term(ErlNifEnv *env, struct ArrowSchema * schema, struct ArrowArray * values, uint64_t level, std::vector<ERL_NIF_TERM> &out_terms, ERL_NIF_TERM &error);
100100

101101
static int get_arrow_array_children_as_list(ErlNifEnv *env, struct ArrowSchema * schema, struct ArrowArray * values, uint64_t level, std::vector<ERL_NIF_TERM> &children, ERL_NIF_TERM &error) {
102102
ERL_NIF_TERM children_term{};
@@ -118,10 +118,18 @@ static int get_arrow_array_children_as_list(ErlNifEnv *env, struct ArrowSchema *
118118
for (int64_t child_i = 0; child_i < schema->n_children; child_i++) {
119119
struct ArrowSchema * child_schema = schema->children[child_i];
120120
struct ArrowArray * child_values = values->children[child_i];
121-
children[child_i] = arrow_array_to_nif_term(env, child_schema, child_values, level + 1);
121+
std::vector<ERL_NIF_TERM> childrens;
122+
if (arrow_array_to_nif_term(env, child_schema, child_values, level + 1, childrens, error) == 1) {
123+
return 1;
124+
}
125+
126+
if (childrens.size() == 0) {
127+
children[child_i] = childrens[0];
128+
} else {
129+
children[child_i] = enif_make_tuple2(env, childrens[0], childrens[1]);
130+
}
122131
}
123132
}
124-
// children_term = enif_make_list_from_array(env, children.data(), (unsigned)schema->n_children);
125133

126134
return 0;
127135
}
@@ -142,7 +150,7 @@ static ERL_NIF_TERM get_arrow_array_map_children(ErlNifEnv *env, struct ArrowSch
142150
if (values->children == nullptr) {
143151
return erlang::nif::error(env, "invalid ArrowArray (map), values->children == nullptr");
144152
}
145-
if (schema->n_children != 1) {
153+
if (values->n_children != 1) {
146154
return erlang::nif::error(env, "invalid ArrowArray (map), values->n_children != 1");
147155
}
148156

@@ -152,7 +160,6 @@ static ERL_NIF_TERM get_arrow_array_map_children(ErlNifEnv *env, struct ArrowSch
152160
return erlang::nif::error(env, "invalid ArrowSchema (map), its single child is not named entries");
153161
}
154162

155-
printf("entries_values->n_children: %d\r\n", entries_values->n_children);
156163
std::vector<ERL_NIF_TERM> nif_keys, nif_values;
157164
bool failed = false;
158165
for (int64_t child_i = 0; child_i < entries_values->n_children; child_i++) {
@@ -177,7 +184,7 @@ static ERL_NIF_TERM get_arrow_array_map_children(ErlNifEnv *env, struct ArrowSch
177184

178185
if (!failed) {
179186
if (nif_keys.size() != nif_values.size()) {
180-
return erlang::nif::error(env, "map contains duplicated keys");
187+
return erlang::nif::error(env, "number of keys and values doesn't match");
181188
}
182189

183190
if (!enif_make_map_from_arrays(env, nif_keys.data(), nif_values.data(), (unsigned)nif_keys.size(), &map_out)) {
@@ -190,18 +197,100 @@ static ERL_NIF_TERM get_arrow_array_map_children(ErlNifEnv *env, struct ArrowSch
190197
}
191198
}
192199

193-
ERL_NIF_TERM arrow_array_to_nif_term(ErlNifEnv *env, struct ArrowSchema * schema, struct ArrowArray * values, uint64_t level) {
200+
static ERL_NIF_TERM get_arrow_array_union_children(ErlNifEnv *env, struct ArrowSchema * schema, struct ArrowArray * values, uint64_t level) {
201+
ERL_NIF_TERM error{}, map_out{};
202+
if (schema->n_children > 0 && schema->children == nullptr) {
203+
return erlang::nif::error(env, "invalid ArrowSchema (union), schema->children == nullptr while schema->n_children > 0 ");
204+
}
205+
if (values->n_children > 0 && values->children == nullptr) {
206+
return erlang::nif::error(env, "invalid ArrowArray (union), values->children == nullptr while values->n_children > 0");
207+
}
208+
209+
std::vector<ERL_NIF_TERM> nif_keys(values->n_children), nif_values2(values->n_children);
210+
std::vector<ERL_NIF_TERM> field_values;
211+
bool failed = false;
212+
for (int64_t child_i = 0; child_i < values->n_children; child_i++) {
213+
struct ArrowSchema * entry_schema = schema->children[child_i];
214+
struct ArrowArray * entry_values = values->children[child_i];
215+
nif_keys[child_i] = erlang::nif::make_binary(env, entry_schema->name);
216+
if (arrow_array_to_nif_term(env, entry_schema, entry_values, level + 1, field_values, error) == 1) {
217+
return error;
218+
}
219+
220+
if (field_values.size() == 0) {
221+
nif_values2[child_i] = field_values[0];
222+
} else {
223+
nif_values2[child_i] = field_values[1];
224+
}
225+
}
226+
227+
if (!failed) {
228+
if (!enif_make_map_from_arrays(env, nif_keys.data(), nif_values2.data(), (unsigned)nif_keys.size(), &map_out)) {
229+
return erlang::nif::error(env, "union contains duplicated fields");
230+
} else {
231+
return map_out;
232+
}
233+
} else {
234+
return erlang::nif::error(env, "invalid union");
235+
}
236+
}
237+
238+
static ERL_NIF_TERM get_arrow_array_list_children(ErlNifEnv *env, struct ArrowSchema * schema, struct ArrowArray * values, uint64_t level) {
239+
ERL_NIF_TERM error{};
240+
if (schema->children == nullptr) {
241+
return erlang::nif::error(env, "invalid ArrowSchema (list), schema->children == nullptr");
242+
}
243+
if (schema->n_children != 1) {
244+
return erlang::nif::error(env, "invalid ArrowSchema (list), schema->n_children != 1");
245+
}
246+
if (values->children == nullptr) {
247+
return erlang::nif::error(env, "invalid ArrowArray (list), values->children == nullptr");
248+
}
249+
if (values->n_children != 1) {
250+
return erlang::nif::error(env, "invalid ArrowArray (list), values->n_children != 1");
251+
}
252+
253+
struct ArrowSchema * items_schema = schema->children[0];
254+
struct ArrowArray * items_values = values->children[0];
255+
if (strncmp("item", items_schema->name, 4) != 0) {
256+
return erlang::nif::error(env, "invalid ArrowSchema (list), its single child is not named item");
257+
}
258+
259+
std::vector<ERL_NIF_TERM> children(items_values->n_children);
260+
bool failed = false;
261+
for (int64_t child_i = 0; child_i < items_values->n_children; child_i++) {
262+
struct ArrowSchema * item_schema = items_schema->children[child_i];
263+
struct ArrowArray * item_values = items_values->children[child_i];
264+
265+
std::vector<ERL_NIF_TERM> childrens;
266+
if (arrow_array_to_nif_term(env, item_schema, item_values, level + 1, childrens, error) == 1) {
267+
return error;
268+
}
269+
270+
if (childrens.size() == 0) {
271+
children[child_i] = childrens[0];
272+
} else {
273+
children[child_i] = enif_make_tuple2(env, childrens[0], childrens[1]);
274+
}
275+
}
276+
277+
return enif_make_list_from_array(env, children.data(), (unsigned)items_values->n_children);
278+
}
279+
280+
int arrow_array_to_nif_term(ErlNifEnv *env, struct ArrowSchema * schema, struct ArrowArray * values, uint64_t level, std::vector<ERL_NIF_TERM> &out_terms, ERL_NIF_TERM &error) {
194281
if (schema == nullptr) {
195-
return erlang::nif::error(env, "invalid ArrowSchema (nullptr) when invoking next");
282+
error = erlang::nif::error(env, "invalid ArrowSchema (nullptr) when invoking next");
283+
return 1;
196284
}
197285
if (values == nullptr) {
198-
return erlang::nif::error(env, "invalid ArrowArray (nullptr) when invoking next");
286+
error = erlang::nif::error(env, "invalid ArrowArray (nullptr) when invoking next");
287+
return 1;
199288
}
200289

201290
const char* format = schema->format ? schema->format : "";
202291
const char* name = schema->name ? schema->name : "";
203292

204-
ERL_NIF_TERM current_term{}, children_term{}, error{};
293+
ERL_NIF_TERM current_term{}, children_term{};
205294
std::vector<ERL_NIF_TERM> children;
206295

207296
bool has_validity_bitmap = values->null_count != 0 && values->null_count != -1;
@@ -342,39 +431,21 @@ ERL_NIF_TERM arrow_array_to_nif_term(ErlNifEnv *env, struct ArrowSchema * schema
342431
// only handle and return children if this is a struct
343432
is_struct = true;
344433
if (get_arrow_array_children_as_list(env, schema, values, level, children, error) == 1) {
345-
return error;
434+
return 1;
346435
}
347436
children_term = enif_make_list_from_array(env, children.data(), (unsigned)schema->n_children);
348-
//get_arrow_array_children_as_list(env, schema, values, level, children, error);
349437
} else if (strncmp("+m", format, 2) == 0) {
350-
// if (get_arrow_array_children_as_list(env, schema, values, level, children, error) == 1) {
351-
// return error;
352-
// }
353-
// children_term = enif_make_list_from_array(env, children.data(), (unsigned)schema->n_children);
354438
children_term = get_arrow_array_map_children(env, schema, values, level);
355439
} else if (strncmp("+l", format, 2) == 0 || strncmp("+L", format, 2) == 0) {
356-
if (get_arrow_array_children_as_list(env, schema, values, level, children, error) == 1) {
357-
return error;
358-
}
359-
children_term = enif_make_list_from_array(env, children.data(), (unsigned)schema->n_children);
360-
// children_term = get_arrow_array_children_as_list(env, schema, values, level);
440+
children_term = get_arrow_array_list_children(env, schema, values, level);
361441
} else {
362442
format_processed = false;
363443
}
364444
} else if (format_len >= 4) {
365445
if (strncmp("+w:", format, 3) == 0) {
366-
if (get_arrow_array_children_as_list(env, schema, values, level, children, error) == 1) {
367-
return error;
368-
}
369-
children_term = enif_make_list_from_array(env, children.data(), (unsigned)schema->n_children);
370-
// children_term = get_arrow_array_children_as_list(env, schema, values, level);
446+
children_term = get_arrow_array_list_children(env, schema, values, level);
371447
} else if (format_len > 4 && (strncmp("+ud:", format, 4) == 0 || strncmp("+us:", format, 4) == 0)) {
372-
// todo: get as map
373-
if (get_arrow_array_children_as_list(env, schema, values, level, children, error) == 1) {
374-
return error;
375-
}
376-
children_term = enif_make_list_from_array(env, children.data(), (unsigned)schema->n_children);
377-
// children_term = get_arrow_array_children_as_list(env, schema, values, level);
448+
children_term = get_arrow_array_union_children(env, schema, values, level);
378449
} else {
379450
format_processed = false;
380451
}
@@ -395,21 +466,21 @@ ERL_NIF_TERM arrow_array_to_nif_term(ErlNifEnv *env, struct ArrowSchema * schema
395466
// printf("buffers: %p\r\n", values->buffers);
396467
}
397468

469+
out_terms.clear();
470+
398471
if (is_struct) {
399-
return children_term;
472+
out_terms.emplace_back(children_term);
400473
} else {
401474
if (schema->children) {
402-
return enif_make_tuple2(env,
403-
erlang::nif::make_binary(env, name),
404-
children_term
405-
);
475+
out_terms.emplace_back(erlang::nif::make_binary(env, name));
476+
out_terms.emplace_back(children_term);
406477
} else {
407-
return enif_make_tuple2(env,
408-
erlang::nif::make_binary(env, name),
409-
current_term
410-
);
478+
out_terms.emplace_back(erlang::nif::make_binary(env, name));
479+
out_terms.emplace_back(current_term);
411480
}
412481
}
482+
483+
return 0;
413484
}
414485

415486
static ERL_NIF_TERM adbc_database_new(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
@@ -804,8 +875,19 @@ static ERL_NIF_TERM adbc_arrow_array_stream_next(ErlNifEnv *env, int argc, const
804875
}
805876
}
806877

878+
std::vector<ERL_NIF_TERM> out_terms;
879+
807880
auto schema = (struct ArrowSchema*)res->private_data;
808-
ret = arrow_array_to_nif_term(env, schema, &out, 0);
881+
if (arrow_array_to_nif_term(env, schema, &out, 0, out_terms, error) == 1) {
882+
if (out.release) out.release(&out);
883+
return error;
884+
}
885+
886+
if (out_terms.size() == 1) {
887+
ret = out_terms[0];
888+
} else {
889+
ret = enif_make_tuple2(env, out_terms[0], out_terms[1]);
890+
}
809891

810892
if (out.release) {
811893
out.release(&out);

test/adbc_connection_test.exs

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,15 @@ defmodule Adbc.Connection.Test do
4949
num_rows: nil,
5050
data: %{
5151
"info_name" => [0, 1, 100, 101, 102],
52-
"info_value" => [
53-
{"string_value",
54-
# ["SQLite", "3.39.2", "ADBC SQLite Driver", "(unknown)", "0.2.0-SNAPSHOT"]},
55-
["SQLite", _, "ADBC SQLite Driver", _, _]},
56-
{"bool_value", []},
57-
{"int64_value", []},
58-
{"int32_bitmask", []},
59-
{"string_list", [{"item", []}]},
60-
{"int32_to_int32_list_map", %{}}
61-
]
52+
"info_value" => %{
53+
"bool_value" => [],
54+
"int32_bitmask" => [],
55+
"int32_to_int32_list_map" => %{},
56+
"int64_value" => [],
57+
"string_list" => [],
58+
# ["SQLite", "3.39.2", "ADBC SQLite Driver", "(unknown)", "0.2.0-SNAPSHOT"]},
59+
"string_value" => ["SQLite", _, "ADBC SQLite Driver", _, _]
60+
}
6261
}
6362
}} = Connection.get_info(conn)
6463
end
@@ -71,14 +70,14 @@ defmodule Adbc.Connection.Test do
7170
num_rows: nil,
7271
data: %{
7372
"info_name" => [0],
74-
"info_value" => [
75-
{"string_value", ["SQLite"]},
76-
{"bool_value", []},
77-
{"int64_value", []},
78-
{"int32_bitmask", []},
79-
{"string_list", [{"item", []}]},
80-
{"int32_to_int32_list_map", %{}}
81-
]
73+
"info_value" => %{
74+
"bool_value" => [],
75+
"int32_bitmask" => [],
76+
"int32_to_int32_list_map" => %{},
77+
"int64_value" => [],
78+
"string_list" => [],
79+
"string_value" => ["SQLite"]
80+
}
8281
}
8382
}} = Connection.get_info(conn, [0])
8483
end

0 commit comments

Comments
 (0)