Skip to content

Commit f402975

Browse files
Simplified code and fixed bug when fetching numeric data that has no
decimal point but the Arrow array has scale > 0.
1 parent 767be68 commit f402975

File tree

2 files changed

+39
-39
lines changed

2 files changed

+39
-39
lines changed

doc/src/release_notes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ Common Changes
6060
by the consumer. This avoids a segfault seen in some circumstances.
6161
- Fixed bug when deciding Arrow datatype for numeric expressions
6262
(`issue 510 <https://github.com/oracle/python-oracledb/issues/510>`__)
63+
- Fixed bug when fetching numeric data that has no decimal point but the
64+
Arrow array has scale > 0
6365

6466
Note the data frame support in python-oracledb 3.3 is a pre-release, and
6567
may change in a future version.

src/oracledb/impl/base/converters.pyx

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -159,50 +159,48 @@ cdef int convert_number_to_arrow_decimal(ArrowArrayImpl arrow_array,
159159
Converts a NUMBER value stored in the buffer to Arrow DECIMAL128.
160160
"""
161161
cdef:
162-
char_type c
163-
bint has_sign = 0
164-
char_type digits[39] # 38 digits + sign
165162
OracleNumber *value = &buffer.as_number
166-
uint8_t num_chars = 0, decimal_point_index = 0, allowed_max_chars = 0
167-
int64_t actual_scale = 0
163+
uint8_t num_digits, allowed_max_chars
164+
char_type digits[40]
165+
uint8_t actual_scale
166+
167+
# determine if the number can be represented as an Arrow decimal128 value
168+
# only 38 decimal digits are permitted (excluding the sign and decimal
169+
# point)
170+
allowed_max_chars = 38
171+
if value.chars[0] == b'-':
172+
allowed_max_chars += 1
173+
if not value.is_integer:
174+
allowed_max_chars += 1
175+
if value.is_max_negative_value or value.num_chars > allowed_max_chars:
176+
raise ValueError("Value cannot be represented as Arrow Decimal128")
168177

169-
if value.chars[0] == 45: # minus sign
170-
has_sign = True
178+
# integers can be handled directly
179+
if value.is_integer and arrow_array.scale == 0:
180+
return arrow_array.append_decimal(value.chars, value.num_chars)
171181

182+
# Arrow expects a string of digits without the decimal point; if the number
183+
# does not contain at least the number of digits after the decimal point
184+
# required by the scale of the Arrow array, zeros are appended
172185
if value.is_integer:
173-
if has_sign:
174-
allowed_max_chars = 39
175-
else:
176-
allowed_max_chars = 38
177-
else: # decimal point
178-
if has_sign:
179-
allowed_max_chars = 40
180-
else:
181-
allowed_max_chars = 39
182-
183-
# Arrow Decimal128 can only represent values with 38 decimal digits
184-
if value.is_max_negative_value or value.num_chars > allowed_max_chars:
185-
raise ValueError("Value cannot be represented as "
186-
"Arrow Decimal128")
187-
if value.is_integer:
188-
arrow_array.append_decimal(value.chars, value.num_chars)
186+
actual_scale = 0
187+
num_digits = value.num_chars
189188
else:
190-
for i in range(value.num_chars):
191-
c = value.chars[i]
192-
# count all characters except the decimal point
193-
if c != 46:
194-
digits[num_chars] = c
195-
num_chars += 1
196-
else:
197-
decimal_point_index = i
198-
199-
# Append any trailing zeros.
200-
actual_scale = num_chars - decimal_point_index
201-
for i in range(abs(arrow_array.scale) - actual_scale):
202-
digits[num_chars] = b'0'
203-
num_chars += 1
204-
arrow_array.append_decimal(digits, num_chars)
205-
189+
actual_scale = 0
190+
while True:
191+
num_digits = value.num_chars - actual_scale - 1
192+
if value.chars[num_digits] == b'.':
193+
break
194+
actual_scale += 1
195+
memcpy(digits, value.chars, num_digits)
196+
if actual_scale > 0:
197+
memcpy(&digits[num_digits], &value.chars[num_digits + 1], actual_scale)
198+
num_digits += actual_scale
199+
while actual_scale < arrow_array.scale:
200+
digits[num_digits] = b'0'
201+
num_digits += 1
202+
actual_scale += 1
203+
arrow_array.append_decimal(digits, num_digits)
206204

207205

208206
cdef int convert_number_to_arrow_double(ArrowArrayImpl arrow_array,

0 commit comments

Comments
 (0)