You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[C++] RecordBatch::MakeEmpty() may drop ordered flag and unsignedness of dictionary types when creating dictionary-encoded column #49674
Describe the bug, including details regarding any error messages, version, and platform.
Found when working on dictionary encoding in nanoarrow in apache/arrow-nanoarrow#861 ...I have yet to confirm independently of that. The bit of code this affects for me checking a roundtrip of an empty record batch through IPC...I am not sure it actually makes sense for any practical purpose to have an empty (but ordered) dictionary.
Hi @paleolimbot, just to confirm the expected behavior here: should RecordBatch::MakeEmpty() preserve the ordered flag for DictionaryType, or is it acceptable for that information to be lost for empty batches?
I think it should be preserved...having the schema of a batch not equal the schema used to create it caused errors in my tests and I think is the intention of MakeEmpty.
I tried to reproduce this using RecordBatch::MakeEmpty() and IPC roundtrips with ordered dictionary types, but I wasn’t able to observe the ordered flag being dropped.
Is this specific to a case where the dictionary array has actual dictionary values (e.g., non-empty dictionary with empty indices), or does it require a particular nanoarrow workflow?
CoPilot via Opus 4.5 generated this (rather dramatic, if I may say so) confirmation of the bug. Basically, the schema is correct but NOT the data type of column.
// Diagnostic test to demonstrate Arrow C++ MakeEmpty() bug
// The schema has ordered=1, but the array type created by MakeEmpty() has ordered=0
TEST(NanoarrowIpcTest, ArrowMakeEmptyLosesOrderedFlag) {
// Create an ordered dictionary type
auto data_type = arrow::dictionary(arrow::int32(), arrow::utf8(), /*ordered=*/true);
std::shared_ptr<arrow::Schema> schema =
arrow::schema({arrow::field("col", data_type)});
// MakeEmpty should create a batch with ordered=true arrays
auto maybe_empty = arrow::RecordBatch::MakeEmpty(schema);
ASSERT_TRUE(maybe_empty.ok()) << maybe_empty.status();
auto empty = maybe_empty.ValueUnsafe();
// Schema correctly has ordered=1
auto schema_dict_type =
std::static_pointer_cast<arrow::DictionaryType>(empty->schema()->field(0)->type());
EXPECT_TRUE(schema_dict_type->ordered()) << "Schema should have ordered=true";
// BUG: Array type has ordered=0
auto array_dict_type =
std::static_pointer_cast<arrow::DictionaryType>(empty->column(0)->type());
// This is the bug in Arrow C++ MakeEmpty() - the array type loses ordered flag
// https://github.com/apache/arrow/issues/49674
EXPECT_FALSE(array_dict_type->ordered())
<< "BUG CONFIRMED: MakeEmpty() array has ordered=0 (should be 1)";
}
That makes sense, I was only checking the schema before, but I can reproduce the issue when inspecting the array type. I’ll take a closer look at how the array is constructed in MakeEmpty().
Hi @paleolimbot, I was able to reproduce this. The issue occurs because DictionaryBuilder reconstructs the DictionaryType without preserving the ordered flag (it defaults to false). Since MakeEmptyArray relies on builder->Finish(), the resulting array loses this information.
I implemented a fix in MakeEmptyArray to restore the original type after building the array, and added a test that verifies the ordered flag is preserved.
Before opening a PR, I wanted to confirm whether this scoped fix is preferred, or if you'd rather address it at the DictionaryBuilder level.
changed the title [-]`RecordBatch::MakeEmpty()` may drop ordered flag when creating dictionary-encoded column[/-][+][C++] `RecordBatch::MakeEmpty()` may drop ordered flag when creating dictionary-encoded column[/+]on Apr 18, 2026
I'm not an expert on these internals, but I can see how the DictionaryBuilder might not be able to guarantee the ordering of the keys that come out of it while preserving the dictionary indices emitted for previous rows. It seems like your approach is reasonable...make a PR and we can discuss there!
changed the title [-][C++] `RecordBatch::MakeEmpty()` may drop ordered flag when creating dictionary-encoded column[/-][+][C++] `RecordBatch::MakeEmpty()` may drop ordered flag and unsignedness of dictionary types when creating dictionary-encoded column[/+]on May 12, 2026
Activity
Reranko05 commentedon Apr 17, 2026
Hi @paleolimbot, just to confirm the expected behavior here: should
RecordBatch::MakeEmpty()preserve theorderedflag forDictionaryType, or is it acceptable for that information to be lost for empty batches?paleolimbot commentedon Apr 17, 2026
I think it should be preserved...having the schema of a batch not equal the schema used to create it caused errors in my tests and I think is the intention of MakeEmpty.
Reranko05 commentedon Apr 17, 2026
Thanks @paleolimbot for confirming, I’ll try to reproduce this and look into where the ordered flag is being dropped during MakeEmpty().
Reranko05 commentedon Apr 17, 2026
I tried to reproduce this using
RecordBatch::MakeEmpty()and IPC roundtrips with ordered dictionary types, but I wasn’t able to observe the ordered flag being dropped.Is this specific to a case where the dictionary array has actual dictionary values (e.g., non-empty dictionary with empty indices), or does it require a particular nanoarrow workflow?
paleolimbot commentedon Apr 17, 2026
Thank you for looking!
It looks like the place I ran across that was here:
https://github.com/apache/arrow-nanoarrow/pull/861/changes#diff-809791cd3b01ea5de2038a046f68a5e5649126a74d9e2ac9ac18217c65f395dfR1326
...where there's quite a few nanoarrow things going on too. I will double check that from my end to make sure this is actually an Arrow C++ issue 😬
Reranko05 commentedon Apr 17, 2026
Okay, will wait for your confirmation on whether this turns out to be on the Arrow C++ side.
paleolimbot commentedon Apr 17, 2026
CoPilot via Opus 4.5 generated this (rather dramatic, if I may say so) confirmation of the bug. Basically, the schema is correct but NOT the data type of column.
Reranko05 commentedon Apr 18, 2026
That makes sense, I was only checking the schema before, but I can reproduce the issue when inspecting the array type. I’ll take a closer look at how the array is constructed in MakeEmpty().
Reranko05 commentedon Apr 18, 2026
Hi @paleolimbot, I was able to reproduce this. The issue occurs because
DictionaryBuilderreconstructs theDictionaryTypewithout preserving theorderedflag (it defaults to false). SinceMakeEmptyArrayrelies onbuilder->Finish(), the resulting array loses this information.I implemented a fix in
MakeEmptyArrayto restore the original type after building the array, and added a test that verifies the ordered flag is preserved.Before opening a PR, I wanted to confirm whether this scoped fix is preferred, or if you'd rather address it at the
DictionaryBuilderlevel.[-]`RecordBatch::MakeEmpty()` may drop ordered flag when creating dictionary-encoded column[/-][+][C++] `RecordBatch::MakeEmpty()` may drop ordered flag when creating dictionary-encoded column[/+]paleolimbot commentedon Apr 18, 2026
I'm not an expert on these internals, but I can see how the DictionaryBuilder might not be able to guarantee the ordering of the keys that come out of it while preserving the dictionary indices emitted for previous rows. It seems like your approach is reasonable...make a PR and we can discuss there!
Reranko05 commentedon Apr 19, 2026
Hi @paleolimbot, I have opened a PR for this issue #49798
apacheGH-41017: Add RecordBatch::MakeEmpty test for dictionary ordere…
apacheGH-41017: Add RecordBatch::MakeEmpty test for dictionary ordere…
paleolimbot commentedon May 12, 2026
in apache/arrow-nanoarrow#882 I also found that it may be dropping the unsigned-ness from unsigned dictionary types!
[-][C++] `RecordBatch::MakeEmpty()` may drop ordered flag when creating dictionary-encoded column[/-][+][C++] `RecordBatch::MakeEmpty()` may drop ordered flag and unsignedness of dictionary types when creating dictionary-encoded column[/+]