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));

Reply via email to