Skip to content

Commit

Permalink
Support writing numerics with larger scale than precision (#98)
Browse files Browse the repository at this point in the history
In parquet, we cannot write numeric with larger scale than precision. But postgres allows it. We can adjust the numeric typmod in that case.
  • Loading branch information
aykut-bozkurt authored Jan 24, 2025
1 parent e775b0e commit fa728df
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 11 deletions.
21 changes: 21 additions & 0 deletions src/pgrx_tests/copy_type_roundtrip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,27 @@ mod tests {
test_table.assert_expected_and_result_rows();
}

#[cfg(feature = "pg14")]
#[pg_test]
#[should_panic = "NUMERIC scale 8 must be between 0 and precision 5"]
fn test_numeric_with_larger_scale() {
let test_table = TestTable::<AnyNumeric>::new("numeric(5, 8)".into());
test_table.insert(
"INSERT INTO test_expected (a) VALUES (0.00012345), (0.00012340), (-0.00012345), (-0.00012340), (null), (0);",
);
test_table.assert_expected_and_result_rows();
}

#[cfg(not(feature = "pg14"))]
#[pg_test]
fn test_numeric_with_larger_scale() {
let test_table = TestTable::<AnyNumeric>::new("numeric(5, 8)".into());
test_table.insert(
"INSERT INTO test_expected (a) VALUES (0.00012345), (0.00012340), (-0.00012345), (-0.00012340), (null), (0);",
);
test_table.assert_expected_and_result_rows();
}

#[pg_test]
#[should_panic = "Special numeric values like NaN, Inf, -Inf are not allowed"]
fn test_numeric_nan() {
Expand Down
23 changes: 12 additions & 11 deletions src/type_compat/pg_arrow_type_conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,19 @@ pub(crate) fn extract_precision_and_scale_from_numeric_typmod(typmod: i32) -> (u
let mut precision = extract_precision_from_numeric_typmod(typmod);
let mut scale = extract_scale_from_numeric_typmod(typmod);

// Even if PG allows negative scale, arrow does not. We adjust precision by adding scale to it.
// Even if PG allows negative scale, arrow does not.
// We adjust precision by adding scale to it and set scale to 0.
// e.g. "123.34::numeric(3,-2)" becomes "100::numeric(5,0)"
if scale < 0 {
adjust_precision_and_scale_if_negative_scale(&mut precision, &mut scale);
precision += scale.abs();
scale = 0;
}

// Even if PG allows scale to be greater than precision, arrow does not.
// We set precision to the same value as scale.
// e.g. "0.0023::numeric(2,4)" becomes "0.0023::numeric(4,4)"
if scale > precision {
precision = scale;
}

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

// adjust_precision_and_scale_if_negative_scale adjusts precision and scale if scale is negative.
// Even if PG allows negative scale, arrow does not. We adjust precision by adding scale to it.
fn adjust_precision_and_scale_if_negative_scale(precision: &mut i32, scale: &mut i32) {
if *scale < 0 {
*precision += scale.abs();
*scale = 0;
}
}

fn is_unbounded_numeric_typmod(typmod: i32) -> bool {
typmod == -1
}

0 comments on commit fa728df

Please sign in to comment.