Skip to content

Commit fa728df

Browse files
Support writing numerics with larger scale than precision (#98)
In parquet, we cannot write numeric with larger scale than precision. But postgres allows it. We can adjust the numeric typmod in that case.
1 parent e775b0e commit fa728df

File tree

2 files changed

+33
-11
lines changed

2 files changed

+33
-11
lines changed

src/pgrx_tests/copy_type_roundtrip.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,27 @@ mod tests {
987987
test_table.assert_expected_and_result_rows();
988988
}
989989

990+
#[cfg(feature = "pg14")]
991+
#[pg_test]
992+
#[should_panic = "NUMERIC scale 8 must be between 0 and precision 5"]
993+
fn test_numeric_with_larger_scale() {
994+
let test_table = TestTable::<AnyNumeric>::new("numeric(5, 8)".into());
995+
test_table.insert(
996+
"INSERT INTO test_expected (a) VALUES (0.00012345), (0.00012340), (-0.00012345), (-0.00012340), (null), (0);",
997+
);
998+
test_table.assert_expected_and_result_rows();
999+
}
1000+
1001+
#[cfg(not(feature = "pg14"))]
1002+
#[pg_test]
1003+
fn test_numeric_with_larger_scale() {
1004+
let test_table = TestTable::<AnyNumeric>::new("numeric(5, 8)".into());
1005+
test_table.insert(
1006+
"INSERT INTO test_expected (a) VALUES (0.00012345), (0.00012340), (-0.00012345), (-0.00012340), (null), (0);",
1007+
);
1008+
test_table.assert_expected_and_result_rows();
1009+
}
1010+
9901011
#[pg_test]
9911012
#[should_panic = "Special numeric values like NaN, Inf, -Inf are not allowed"]
9921013
fn test_numeric_nan() {

src/type_compat/pg_arrow_type_conversions.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -322,9 +322,19 @@ pub(crate) fn extract_precision_and_scale_from_numeric_typmod(typmod: i32) -> (u
322322
let mut precision = extract_precision_from_numeric_typmod(typmod);
323323
let mut scale = extract_scale_from_numeric_typmod(typmod);
324324

325-
// Even if PG allows negative scale, arrow does not. We adjust precision by adding scale to it.
325+
// Even if PG allows negative scale, arrow does not.
326+
// We adjust precision by adding scale to it and set scale to 0.
327+
// e.g. "123.34::numeric(3,-2)" becomes "100::numeric(5,0)"
326328
if scale < 0 {
327-
adjust_precision_and_scale_if_negative_scale(&mut precision, &mut scale);
329+
precision += scale.abs();
330+
scale = 0;
331+
}
332+
333+
// Even if PG allows scale to be greater than precision, arrow does not.
334+
// We set precision to the same value as scale.
335+
// e.g. "0.0023::numeric(2,4)" becomes "0.0023::numeric(4,4)"
336+
if scale > precision {
337+
precision = scale;
328338
}
329339

330340
debug_assert!(precision >= 0);
@@ -345,15 +355,6 @@ fn extract_scale_from_numeric_typmod(typmod: i32) -> i32 {
345355
(((typmod - pg_sys::VARHDRSZ as i32) & 0x7ff) ^ 1024) - 1024
346356
}
347357

348-
// adjust_precision_and_scale_if_negative_scale adjusts precision and scale if scale is negative.
349-
// Even if PG allows negative scale, arrow does not. We adjust precision by adding scale to it.
350-
fn adjust_precision_and_scale_if_negative_scale(precision: &mut i32, scale: &mut i32) {
351-
if *scale < 0 {
352-
*precision += scale.abs();
353-
*scale = 0;
354-
}
355-
}
356-
357358
fn is_unbounded_numeric_typmod(typmod: i32) -> bool {
358359
typmod == -1
359360
}

0 commit comments

Comments
 (0)