This is an automated email from the ASF dual-hosted git repository.
lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git
The following commit(s) were added to refs/heads/main by this push:
new eb524a11bc fix(c/driver/postgresql): fix ingestion of certain negative
decimal values (#4850)
eb524a11bc is described below
commit eb524a11bc6fc0572d8a8962cbe0b13e2833af6b
Author: Bryce Mecum <[email protected]>
AuthorDate: Sun Oct 4 16:28:37 2026 -0700
fix(c/driver/postgresql): fix ingestion of certain negative decimal values
(#4850)
Fixes incorrect ingestion of negative decimal coefficients that are
multiples of 2^64. This affects Decimal256 and the Decimal128 fallback
path. For example, −18446744073709551616 was ingested as zero.
Bug found, debugging done, and changes made with GPT6.1-Sol.
---
.../postgresql/copy/postgres_copy_writer_test.cc | 21 +++++++++++++++++++++
c/driver/postgresql/copy/writer.h | 7 ++++---
c/driver/postgresql/postgresql_test.cc | 12 +++++++++++-
3 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/c/driver/postgresql/copy/postgres_copy_writer_test.cc
b/c/driver/postgresql/copy/postgres_copy_writer_test.cc
index e485d2b80c..e3dcb2966a 100644
--- a/c/driver/postgresql/copy/postgres_copy_writer_test.cc
+++ b/c/driver/postgresql/copy/postgres_copy_writer_test.cc
@@ -180,6 +180,27 @@ static void AssertNumericZeroIntegerGroupsRoundTrip(
}
}
+TEST(PostgresCopyUtilsTest, PostgresCopyWriteNumericNegativeWordCarry) {
+ ArrowDecimal decimal;
+ ArrowDecimalInit(&decimal, 128, 38, 0);
+ ASSERT_EQ(ArrowDecimalSetDigits(&decimal,
ArrowCharView("-18446744073709551616")),
+ NANOARROW_OK);
+ nanoarrow::UniqueArrayView array_view;
+ ArrowArrayViewInitFromType(array_view.get(), NANOARROW_TYPE_DECIMAL128);
+ array_view->buffer_views[1].data.as_uint8 =
+ reinterpret_cast<const uint8_t*>(decimal.words);
+ auto writer = PostgresCopyFieldWriter::Create<
+ PostgresCopyNumericFieldWriter<NANOARROW_TYPE_DECIMAL128>>(
+ array_view.get(), 38, 0, /*disable_fast_path=*/true);
+ nanoarrow::UniqueBuffer buffer;
+ ASSERT_EQ(writer->Write(buffer.get(), 0, nullptr), NANOARROW_OK);
+ const std::vector<uint8_t> expected = {0x00, 0x00, 0x00, 0x12, 0x00, 0x05,
0x00, 0x04,
+ 0x40, 0x00, 0x00, 0x00, 0x07, 0x34,
0x1a, 0x58,
+ 0x02, 0xe1, 0x03, 0xbb, 0x06, 0x50};
+ EXPECT_EQ(std::vector<uint8_t>(buffer->data, buffer->data +
buffer->size_bytes),
+ expected);
+}
+
TEST_F(PostgresCopyTest, PostgresCopyWriteBoolean) {
adbc_validation::Handle<struct ArrowSchema> schema;
adbc_validation::Handle<struct ArrowArray> array;
diff --git a/c/driver/postgresql/copy/writer.h
b/c/driver/postgresql/copy/writer.h
index eadbc528df..a5f9ead1d5 100644
--- a/c/driver/postgresql/copy/writer.h
+++ b/c/driver/postgresql/copy/writer.h
@@ -595,9 +595,10 @@ class PostgresCopyNumericFieldWriter : public
PostgresCopyFieldWriter {
const int16_t sign = ArrowDecimalSign(decimal) > 0 ? kNumericPos :
kNumericNeg;
const bool is_negative = sign == kNumericNeg ? true : false;
if (is_negative) {
- buf[0] = ~buf[0] + 1;
- for (size_t i = 1; i < nwords; i++) {
- buf[i] = ~buf[i];
+ uint64_t carry = 1;
+ for (size_t i = 0; i < nwords; i++) {
+ buf[i] = ~buf[i] + carry;
+ carry = carry && buf[i] == 0;
}
}
diff --git a/c/driver/postgresql/postgresql_test.cc
b/c/driver/postgresql/postgresql_test.cc
index 2e10be9b69..deef9103d6 100644
--- a/c/driver/postgresql/postgresql_test.cc
+++ b/c/driver/postgresql/postgresql_test.cc
@@ -3624,7 +3624,17 @@ static std::initializer_list<DecimalTestCase>
kDecimal256LargeNoScaleCases = {
{
"-1234567890123456789012345678901234567890123456789012345678901234567890123456",
"1234567890123456789012345678901234567890123456789012345678901234567890123456",
- }}};
+ }},
+ {NANOARROW_TYPE_DECIMAL256,
+ 76,
+ 0,
+ {
+ // -18446744073709551617, -18446744073709551616, -18446744073709551615
+ {UINT64_MAX, UINT64_MAX - 1, UINT64_MAX, UINT64_MAX},
+ {0, UINT64_MAX, UINT64_MAX, UINT64_MAX},
+ {1, UINT64_MAX, UINT64_MAX, UINT64_MAX},
+ },
+ {"-18446744073709551617", "-18446744073709551616",
"-18446744073709551615"}}};
INSTANTIATE_TEST_SUITE_P(Decimal128Tests, PostgresDecimalTest,
testing::ValuesIn(kDecimal128Cases));