This is an automated email from the ASF dual-hosted git repository.

paleolimbot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-nanoarrow.git


The following commit(s) were added to refs/heads/main by this push:
     new bed19492 feat: Add support for Decimal32/64 to R package (#717)
bed19492 is described below

commit bed19492ce8fe48d4df6df77c45bc3a8143c8f58
Author: Dewey Dunnington <[email protected]>
AuthorDate: Thu Mar 20 09:59:58 2025 -0500

    feat: Add support for Decimal32/64 to R package (#717)
    
    This PR adds support for decimal32, 64, and 256, and migrates support
    for decimal128 to use nanoarrow's internals rather than call Arrow. This
    removes the last dependency on arrow for conversion from Arrow -> R, for
    which some (undiagnosed as of yet) bug results in very slow conversions
    when many batches are involved (
    https://github.com/apache/arrow-adbc/issues/2508 ). Decimal conversions
    show up in database land quite lot because decimal types are common in
    database output (notably: Snowflake).
    
    To support the types that the arrow package does not yet support
    (Decimal32, 64, and 256), this PR also implements conversion from R ->
    Arrow for decimal types. This conversion is a little hacky because it
    involves strings, but the basic idea is to use the fact that R has all
    the functionality needed (rounding, appending adding zeroes, etc.). This
    code path is probably rare because one has to explicitly opt-in to a
    decimal type but I put it in because it's helpful to ensure we (and
    others) can create test data or specific typed input for a database
    append if required.
    
    ``` r
    library(nanoarrow)
    
    # Can create a decimal array from integer or double
    (dec_array <- as_nanoarrow_array(-10:10, schema = na_decimal32(9, 2)))
    #> <nanoarrow_array decimal32(9, 2)[21]>
    #>  $ length    : int 21
    #>  $ null_count: int 0
    #>  $ offset    : int 0
    #>  $ buffers   :List of 2
    #>   ..$ :<nanoarrow_buffer validity<bool>[null] ``
    #>   ..$ :<nanoarrow_buffer data<decimal32>[21][84 b]> `-1000 -900 -800 
-700 -6...`
    #>  $ dictionary: NULL
    #>  $ children  : list()
    
    # Default conversion to R is a double, which matches what arrow does
    # (and what nanoarrow did before this change for decimal128)
    str(convert_array(dec_array))
    #>  num [1:21] -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 ...
    
    # One can also convert to character(), which keeps better fidelity for
    # out-of-range types.
    str(convert_array(dec_array, character()))
    #>  chr [1:21] "-10.00" "-9.00" "-8.00" "-7.00" "-6.00" "-5.00" "-4.00" ...
    
    # Print method for decimal buffers!
    dec_array$buffers[[2]]
    #> <nanoarrow_buffer data<decimal32>[21][84 b]> `-1000 -900 -800 -700 -600 
-500...`
    ```
    
    <sup>Created on 2025-03-15 with [reprex
    v2.1.1](https://reprex.tidyverse.org)</sup>
---
 r/NAMESPACE                           |   5 +-
 r/R/as-array.R                        |  26 +++++++++
 r/R/buffer.R                          |  18 ++++++
 r/R/convert-array.R                   |  20 -------
 r/R/type.R                            |  60 +++++++++++++++++++-
 r/man/na_type.Rd                      |  22 ++++++--
 r/nanoarrow.Rproj                     |   1 +
 r/src/as_array.c                      |  77 ++++++++++++++++++++++++++
 r/src/convert_array.c                 |   1 -
 r/src/infer_ptype.c                   |   5 +-
 r/src/materialize_chr.h               |  35 +++++++++++-
 r/src/materialize_dbl.h               |  39 +++++++++++++
 r/src/schema.c                        |   4 +-
 r/tests/testthat/_snaps/buffer.md     |  28 ++++++++++
 r/tests/testthat/test-altrep.R        |   1 -
 r/tests/testthat/test-as-array.R      | 100 ++++++++++++++++++++++++++++++++++
 r/tests/testthat/test-buffer.R        |  35 ++++++++++++
 r/tests/testthat/test-convert-array.R |  68 ++++++++++++++++++-----
 r/tests/testthat/test-infer-ptype.R   |   8 +--
 r/tests/testthat/test-type.R          |  23 ++++++++
 src/nanoarrow/common/schema.c         |  16 ++++++
 src/nanoarrow/common/schema_test.cc   |  58 +++++++++++++-------
 22 files changed, 579 insertions(+), 71 deletions(-)

diff --git a/r/NAMESPACE b/r/NAMESPACE
index c76f380e..f1331649 100644
--- a/r/NAMESPACE
+++ b/r/NAMESPACE
@@ -63,7 +63,6 @@ S3method(as_nanoarrow_schema,nanoarrow_schema)
 S3method(as_nanoarrow_schema,nanoarrow_vctr)
 S3method(c,nanoarrow_vctr)
 S3method(convert_array,default)
-S3method(convert_array,double)
 S3method(convert_array,factor)
 S3method(convert_array,nanoarrow_vctr)
 S3method(convert_array,vctrs_partial_frame)
@@ -155,6 +154,8 @@ export(na_date32)
 export(na_date64)
 export(na_decimal128)
 export(na_decimal256)
+export(na_decimal32)
+export(na_decimal64)
 export(na_dense_union)
 export(na_dictionary)
 export(na_double)
@@ -173,8 +174,10 @@ export(na_interval_month_day_nano)
 export(na_interval_months)
 export(na_large_binary)
 export(na_large_list)
+export(na_large_list_view)
 export(na_large_string)
 export(na_list)
+export(na_list_view)
 export(na_map)
 export(na_na)
 export(na_sparse_union)
diff --git a/r/R/as-array.R b/r/R/as-array.R
index e4deff3e..3e350ef7 100644
--- a/r/R/as-array.R
+++ b/r/R/as-array.R
@@ -475,3 +475,29 @@ as_nanoarrow_array_from_c <- function(x, schema) {
 
   result
 }
+
+# Helper to allow us to use nanoarrow's string parser, which parses integers
+# to set decimal storage but not the slightly more useful case of parsing
+# things with decimal points yet.
+storage_integer_for_decimal <- function(numbers, scale) {
+  rounded_formatted <- storage_decimal_for_decimal(numbers, scale)
+  gsub(".", "", rounded_formatted, fixed = TRUE)
+}
+
+storage_decimal_for_decimal <- function(numbers, scale) {
+  if (scale > 0) {
+    rounded_formatted <- sprintf("%0.*f", scale, numbers)
+    rounded_formatted[is.na(numbers)] <- NA_character_
+  } else {
+    rounded <- round(numbers, scale)
+    is_zero <- !is.na(rounded) & rounded == 0
+    rounded_formatted <- as.character(rounded)
+    rounded_formatted[!is_zero] <- gsub(
+      paste0("0{", -scale, "}$"),
+      "",
+      rounded_formatted[!is_zero]
+    )
+  }
+
+  rounded_formatted
+}
diff --git a/r/R/buffer.R b/r/R/buffer.R
index 2267d742..d3e7fab9 100644
--- a/r/R/buffer.R
+++ b/r/R/buffer.R
@@ -225,6 +225,24 @@ as_nanoarrow_array.nanoarrow_buffer <- function(x, ..., 
schema = NULL) {
         buffers = list(NULL, offsets, x)
       )
     )
+  } else if (data_type %in% c("decimal32", "decimal64", "decimal128", 
"decimal256")) {
+    # Create an array with max precision and scale 0, which results in the
+    # decimal integer value getting displayed.
+    array <- nanoarrow_array_init(
+      na_type(
+        data_type,
+        precision = max_decimal_precision(data_type),
+        scale = 0
+      )
+    )
+    nanoarrow_array_modify(
+      array,
+      list(
+        length = logical_length,
+        null_count = 0,
+        buffers = list(NULL, x)
+      )
+    )
   } else if (data_type %in% c("string_view", "binary_view")) {
     stop("Can't convert buffer of type string_view or binary_view to array")
   } else {
diff --git a/r/R/convert-array.R b/r/R/convert-array.R
index 5e3c1502..6af82f59 100644
--- a/r/R/convert-array.R
+++ b/r/R/convert-array.R
@@ -155,26 +155,6 @@ convert_array.nanoarrow_vctr <- function(array, to, ...) {
   new_nanoarrow_vctr(list(array), schema, class(to))
 }
 
-#' @export
-convert_array.double <- function(array, to, ...) {
-  # Handle conversion from decimal128 via arrow
-  schema <- infer_nanoarrow_schema(array)
-  parsed <- nanoarrow_schema_parse(schema)
-  if (parsed$type == "decimal128") {
-    assert_arrow_installed(
-      sprintf(
-        "convert %s array to object of type double",
-        nanoarrow_schema_formatted(schema)
-      )
-    )
-
-    arrow_array <- as_arrow_array.nanoarrow_array(array)
-    arrow_array$as_vector()
-  } else {
-    NextMethod()
-  }
-}
-
 #' @export
 convert_array.vctrs_partial_frame <- function(array, to, ...) {
   ptype <- infer_nanoarrow_ptype(array)
diff --git a/r/R/type.R b/r/R/type.R
index 77bc0ad8..85d6f1b0 100644
--- a/r/R/type.R
+++ b/r/R/type.R
@@ -65,6 +65,7 @@
 #' na_struct(list(col1 = na_int32()))
 #'
 na_type <- function(type_name, byte_width = NULL, unit = NULL, timezone = NULL,
+                    precision = NULL, scale = NULL,
                     column_types = NULL, item_type = NULL, key_type = NULL,
                     value_type = NULL, index_type = NULL, ordered = NULL,
                     list_size = NULL, keys_sorted = NULL, storage_type = NULL,
@@ -76,6 +77,8 @@ na_type <- function(type_name, byte_width = NULL, unit = 
NULL, timezone = NULL,
     byte_width = byte_width,
     unit = unit,
     timezone = timezone,
+    precision = precision,
+    scale = scale,
     column_types = column_types,
     item_type = item_type,
     key_type = key_type,
@@ -307,6 +310,30 @@ na_timestamp <- function(unit = c("us", "ns", "s", "ms"), 
timezone = "", nullabl
   )
 }
 
+#' @rdname na_type
+#' @export
+na_decimal32 <- function(precision, scale, nullable = TRUE) {
+  .Call(
+    nanoarrow_c_schema_init_decimal,
+    NANOARROW_TYPE$DECIMAL32,
+    as.integer(precision)[1],
+    as.integer(scale)[1],
+    isTRUE(nullable)
+  )
+}
+
+#' @rdname na_type
+#' @export
+na_decimal64 <- function(precision, scale, nullable = TRUE) {
+  .Call(
+    nanoarrow_c_schema_init_decimal,
+    NANOARROW_TYPE$DECIMAL64,
+    as.integer(precision)[1],
+    as.integer(scale)[1],
+    isTRUE(nullable)
+  )
+}
+
 #' @rdname na_type
 #' @export
 na_decimal128 <- function(precision, scale, nullable = TRUE) {
@@ -371,6 +398,22 @@ na_large_list <- function(item_type, nullable = TRUE) {
   schema
 }
 
+#' @rdname na_type
+#' @export
+na_list_view <- function(item_type, nullable = TRUE) {
+  schema <- .Call(nanoarrow_c_schema_init, NANOARROW_TYPE$LIST_VIEW, 
isTRUE(nullable))
+  schema$children[[1]] <- item_type
+  schema
+}
+
+#' @rdname na_type
+#' @export
+na_large_list_view <- function(item_type, nullable = TRUE) {
+  schema <- .Call(nanoarrow_c_schema_init, NANOARROW_TYPE$LARGE_LIST_VIEW, 
isTRUE(nullable))
+  schema$children[[1]] <- item_type
+  schema
+}
+
 #' @rdname na_type
 #' @export
 na_fixed_size_list <- function(item_type, list_size, nullable = TRUE) {
@@ -430,6 +473,17 @@ time_unit_id <- function(time_unit) {
   match(time_unit, c("s", "ms", "us", "ns")) - 1L
 }
 
+max_decimal_precision <- function(type) {
+  switch(
+    type,
+    decimal32 = 9,
+    decimal64 = 18,
+    decimal128 = 38,
+    decimal256 = 76,
+    stop(sprintf("non-decimal type name: %s", type))
+  )
+}
+
 # These values aren't guaranteed to stay stable between nanoarrow versions,
 # so we keep them internal but use them in these functions to simplify the
 # number of C functions we need to build all the types.
@@ -475,7 +529,11 @@ NANOARROW_TYPE <- list(
   INTERVAL_MONTH_DAY_NANO = 38L,
   RUN_END_ENCODED = 39L,
   BINARY_VIEW = 40L,
-  STRING_VIEW = 41L
+  STRING_VIEW = 41L,
+  DECIMAL32 = 42L,
+  DECIMAL64 = 43L,
+  LIST_VIEW = 44L,
+  LARGE_LIST_VIEW = 45L
 )
 
 ARROW_FLAG <- list(
diff --git a/r/man/na_type.Rd b/r/man/na_type.Rd
index ca58123f..51e66bb4 100644
--- a/r/man/na_type.Rd
+++ b/r/man/na_type.Rd
@@ -31,6 +31,8 @@
 \alias{na_interval_day_time}
 \alias{na_interval_month_day_nano}
 \alias{na_timestamp}
+\alias{na_decimal32}
+\alias{na_decimal64}
 \alias{na_decimal128}
 \alias{na_decimal256}
 \alias{na_struct}
@@ -38,6 +40,8 @@
 \alias{na_dense_union}
 \alias{na_list}
 \alias{na_large_list}
+\alias{na_list_view}
+\alias{na_large_list_view}
 \alias{na_fixed_size_list}
 \alias{na_map}
 \alias{na_dictionary}
@@ -49,6 +53,8 @@ na_type(
   byte_width = NULL,
   unit = NULL,
   timezone = NULL,
+  precision = NULL,
+  scale = NULL,
   column_types = NULL,
   item_type = NULL,
   key_type = NULL,
@@ -121,6 +127,10 @@ na_interval_month_day_nano(nullable = TRUE)
 
 na_timestamp(unit = c("us", "ns", "s", "ms"), timezone = "", nullable = TRUE)
 
+na_decimal32(precision, scale, nullable = TRUE)
+
+na_decimal64(precision, scale, nullable = TRUE)
+
 na_decimal128(precision, scale, nullable = TRUE)
 
 na_decimal256(precision, scale, nullable = TRUE)
@@ -135,6 +145,10 @@ na_list(item_type, nullable = TRUE)
 
 na_large_list(item_type, nullable = TRUE)
 
+na_list_view(item_type, nullable = TRUE)
+
+na_large_list_view(item_type, nullable = TRUE)
+
 na_fixed_size_list(item_type, list_size, nullable = TRUE)
 
 na_map(key_type, item_type, keys_sorted = FALSE, nullable = TRUE)
@@ -157,6 +171,10 @@ or 'ns' (nanoseconds).}
 represents a naive point in time (i.e., one that has no associated
 timezone).}
 
+\item{precision}{The total number of digits representable by the decimal type}
+
+\item{scale}{The number of digits after the decimal point in a decimal type}
+
 \item{column_types}{A \code{list()} of 
\link[=as_nanoarrow_schema]{nanoarrow_schema}s.}
 
 \item{item_type}{For \code{\link[=na_list]{na_list()}}, 
\code{\link[=na_large_list]{na_large_list()}}, 
\code{\link[=na_fixed_size_list]{na_fixed_size_list()}},
@@ -190,10 +208,6 @@ Most Arrow extension types define extension metadata as a 
JSON object.}
 
 \item{nullable}{Use \code{FALSE} to assert that this field cannot contain
 null values.}
-
-\item{precision}{The total number of digits representable by the decimal type}
-
-\item{scale}{The number of digits after the decimal point in a decimal type}
 }
 \value{
 A \link[=as_nanoarrow_schema]{nanoarrow_schema}
diff --git a/r/nanoarrow.Rproj b/r/nanoarrow.Rproj
index 69fafd4b..12816155 100644
--- a/r/nanoarrow.Rproj
+++ b/r/nanoarrow.Rproj
@@ -1,4 +1,5 @@
 Version: 1.0
+ProjectId: 66584247-a2ea-4bae-bcbd-86d4e09bc627
 
 RestoreWorkspace: No
 SaveWorkspace: No
diff --git a/r/src/as_array.c b/r/src/as_array.c
index bcafb5a3..24926ee6 100644
--- a/r/src/as_array.c
+++ b/r/src/as_array.c
@@ -46,10 +46,29 @@ static void call_as_nanoarrow_array(SEXP x_sexp, struct 
ArrowArray* array,
   UNPROTECT(3);
 }
 
+static SEXP call_storage_integer_for_decimal(SEXP x_sexp, int scale) {
+  SEXP scale_sexp = PROTECT(Rf_ScalarInteger(scale));
+  SEXP fun = PROTECT(Rf_install("storage_integer_for_decimal"));
+  SEXP call = PROTECT(Rf_lang3(fun, x_sexp, scale_sexp));
+  SEXP result = PROTECT(Rf_eval(call, nanoarrow_ns_pkg));
+  UNPROTECT(4);
+  return result;
+}
+
+static void as_decimal_array(SEXP x_sexp, struct ArrowArray* array, SEXP 
schema_xptr,
+                             struct ArrowSchemaView* schema_view,
+                             struct ArrowError* error);
+
 static void as_array_int(SEXP x_sexp, struct ArrowArray* array, SEXP 
schema_xptr,
                          struct ArrowSchemaView* schema_view, struct 
ArrowError* error) {
   // Consider integer -> numeric types that are easy to implement
   switch (schema_view->type) {
+    case NANOARROW_TYPE_DECIMAL32:
+    case NANOARROW_TYPE_DECIMAL64:
+    case NANOARROW_TYPE_DECIMAL128:
+    case NANOARROW_TYPE_DECIMAL256:
+      as_decimal_array(x_sexp, array, schema_xptr, schema_view, error);
+      return;
     case NANOARROW_TYPE_DOUBLE:
     case NANOARROW_TYPE_FLOAT:
     case NANOARROW_TYPE_HALF_FLOAT:
@@ -215,6 +234,12 @@ static void as_array_dbl(SEXP x_sexp, struct ArrowArray* 
array, SEXP schema_xptr
   // Consider double -> na_double() and double -> na_int64()/na_int32()
   // (mostly so that we can support date/time types with various units)
   switch (schema_view->type) {
+    case NANOARROW_TYPE_DECIMAL32:
+    case NANOARROW_TYPE_DECIMAL64:
+    case NANOARROW_TYPE_DECIMAL128:
+    case NANOARROW_TYPE_DECIMAL256:
+      as_decimal_array(x_sexp, array, schema_xptr, schema_view, error);
+      return;
     case NANOARROW_TYPE_DOUBLE:
     case NANOARROW_TYPE_FLOAT:
     case NANOARROW_TYPE_HALF_FLOAT:
@@ -346,6 +371,58 @@ static void as_array_dbl(SEXP x_sexp, struct ArrowArray* 
array, SEXP schema_xptr
   }
 }
 
+static void as_decimal_array(SEXP x_sexp, struct ArrowArray* array, SEXP 
schema_xptr,
+                             struct ArrowSchemaView* schema_view,
+                             struct ArrowError* error) {
+  // Use R to generate the input we need for ArrowDecimalSetDigits()
+  SEXP x_digits_sexp =
+      PROTECT(call_storage_integer_for_decimal(x_sexp, 
schema_view->decimal_scale));
+
+  struct ArrowDecimal item;
+  ArrowDecimalInit(&item, schema_view->decimal_bitwidth, 
schema_view->decimal_precision,
+                   schema_view->decimal_scale);
+
+  int result = ArrowArrayInitFromType(array, schema_view->type);
+  if (result != NANOARROW_OK) {
+    Rf_error("ArrowArrayInitFromType() failed");
+  }
+
+  result = ArrowArrayStartAppending(array);
+  if (result != NANOARROW_OK) {
+    Rf_error("ArrowArrayStartAppending() failed");
+  }
+
+  int64_t len = Rf_xlength(x_sexp);
+  result = ArrowArrayReserve(array, len);
+  if (result != NANOARROW_OK) {
+    Rf_error("ArrowArrayReserve() failed");
+  }
+
+  struct ArrowStringView item_digits_view;
+  for (int64_t i = 0; i < len; i++) {
+    SEXP item_sexp = STRING_ELT(x_digits_sexp, i);
+    if (item_sexp == NA_STRING) {
+      result = ArrowArrayAppendNull(array, 1);
+    } else {
+      item_digits_view.data = CHAR(item_sexp);
+      item_digits_view.size_bytes = Rf_length(item_sexp);
+      ArrowDecimalSetDigits(&item, item_digits_view);
+      result = ArrowArrayAppendDecimal(array, &item);
+    }
+
+    if (result != NANOARROW_OK) {
+      Rf_error("ArrowArrayAppendDecimal() failed");
+    }
+  }
+
+  UNPROTECT(1);
+
+  result = ArrowArrayFinishBuildingDefault(array, error);
+  if (result != NANOARROW_OK) {
+    Rf_error("ArrowArrayFinishBuildingDefault(): %s", error->message);
+  }
+}
+
 static void as_array_chr(SEXP x_sexp, struct ArrowArray* array, SEXP 
schema_xptr,
                          struct ArrowSchemaView* schema_view, struct 
ArrowError* error) {
   switch (schema_view->type) {
diff --git a/r/src/convert_array.c b/r/src/convert_array.c
index 8e6d0fdb..96fb2cb6 100644
--- a/r/src/convert_array.c
+++ b/r/src/convert_array.c
@@ -23,7 +23,6 @@
 
 #include "altrep.h"
 #include "array.h"
-#include "array_view.h"
 #include "convert.h"
 #include "util.h"
 
diff --git a/r/src/infer_ptype.c b/r/src/infer_ptype.c
index 37ef6e33..69160e72 100644
--- a/r/src/infer_ptype.c
+++ b/r/src/infer_ptype.c
@@ -21,9 +21,7 @@
 
 #include "nanoarrow.h"
 
-#include "altrep.h"
 #include "array.h"
-#include "array_view.h"
 #include "materialize.h"
 #include "schema.h"
 #include "util.h"
@@ -52,7 +50,10 @@ enum VectorType nanoarrow_infer_vector_type(enum ArrowType 
type) {
     case NANOARROW_TYPE_HALF_FLOAT:
     case NANOARROW_TYPE_FLOAT:
     case NANOARROW_TYPE_DOUBLE:
+    case NANOARROW_TYPE_DECIMAL32:
+    case NANOARROW_TYPE_DECIMAL64:
     case NANOARROW_TYPE_DECIMAL128:
+    case NANOARROW_TYPE_DECIMAL256:
       return VECTOR_TYPE_DBL;
 
     case NANOARROW_TYPE_STRING:
diff --git a/r/src/materialize_chr.h b/r/src/materialize_chr.h
index 6fe857bd..91e518bc 100644
--- a/r/src/materialize_chr.h
+++ b/r/src/materialize_chr.h
@@ -22,8 +22,8 @@
 #include <Rinternals.h>
 
 #include <inttypes.h>
-#include <stdint.h>
 
+#include "buffer.h"
 #include "materialize_common.h"
 #include "nanoarrow.h"
 
@@ -64,6 +64,39 @@ static inline int nanoarrow_materialize_chr(struct 
RConverter* converter) {
       return NANOARROW_OK;
     }
 
+    case NANOARROW_TYPE_DECIMAL32:
+    case NANOARROW_TYPE_DECIMAL64:
+    case NANOARROW_TYPE_DECIMAL128:
+    case NANOARROW_TYPE_DECIMAL256: {
+      struct ArrowDecimal item;
+      ArrowDecimalInit(&item, converter->schema_view.decimal_bitwidth,
+                       converter->schema_view.decimal_precision,
+                       converter->schema_view.decimal_scale);
+
+      // Buffer to manage the building of the digits output
+      SEXP buffer_xptr = PROTECT(buffer_owning_xptr());
+      struct ArrowBuffer* digits = (struct 
ArrowBuffer*)R_ExternalPtrAddr(buffer_xptr);
+
+      for (R_xlen_t i = 0; i < dst->length; i++) {
+        if (ArrowArrayViewIsNull(src->array_view, src->offset + i)) {
+          SET_STRING_ELT(dst->vec_sexp, dst->offset + i, NA_STRING);
+        } else {
+          ArrowArrayViewGetDecimalUnsafe(src->array_view, src->offset + i, 
&item);
+          digits->size_bytes = 0;
+          int status = ArrowDecimalAppendStringToBuffer(&item, digits);
+          if (status != NANOARROW_OK) {
+            UNPROTECT(1);
+            return status;
+          }
+
+          SET_STRING_ELT(dst->vec_sexp, dst->offset + i,
+                         Rf_mkCharLen((char*)digits->data, 
(int)digits->size_bytes));
+        }
+      }
+      UNPROTECT(1);
+      return NANOARROW_OK;
+    }
+
     case NANOARROW_TYPE_STRING:
     case NANOARROW_TYPE_LARGE_STRING:
     case NANOARROW_TYPE_STRING_VIEW:
diff --git a/r/src/materialize_dbl.h b/r/src/materialize_dbl.h
index f24ff07d..6d7e09ba 100644
--- a/r/src/materialize_dbl.h
+++ b/r/src/materialize_dbl.h
@@ -21,6 +21,7 @@
 #include <R.h>
 #include <Rinternals.h>
 
+#include "buffer.h"
 #include "materialize_common.h"
 #include "nanoarrow.h"
 
@@ -109,6 +110,44 @@ static inline int nanoarrow_materialize_dbl(struct 
RConverter* converter) {
       }
       break;
 
+    case NANOARROW_TYPE_DECIMAL32:
+    case NANOARROW_TYPE_DECIMAL64:
+    case NANOARROW_TYPE_DECIMAL128:
+    case NANOARROW_TYPE_DECIMAL256: {
+      struct ArrowDecimal item;
+      ArrowDecimalInit(&item, converter->schema_view.decimal_bitwidth,
+                       converter->schema_view.decimal_precision,
+                       converter->schema_view.decimal_scale);
+
+      // Buffer to manage the building of the digits output
+      SEXP buffer_xptr = PROTECT(buffer_owning_xptr());
+      struct ArrowBuffer* digits = (struct 
ArrowBuffer*)R_ExternalPtrAddr(buffer_xptr);
+
+      // A length one character() we'll use as input to Rf_asReal()
+      SEXP decimal_as_chr = PROTECT(Rf_allocVector(STRSXP, 1));
+
+      for (R_xlen_t i = 0; i < dst->length; i++) {
+        if (is_valid != NULL && !ArrowBitGet(is_valid, raw_src_offset + i)) {
+          result[dst->offset + i] = NA_REAL;
+          continue;
+        }
+
+        ArrowArrayViewGetDecimalUnsafe(src->array_view, src->offset + i, 
&item);
+        digits->size_bytes = 0;
+        int status = ArrowDecimalAppendStringToBuffer(&item, digits);
+        if (status != NANOARROW_OK) {
+          UNPROTECT(2);
+          return status;
+        }
+
+        SET_STRING_ELT(decimal_as_chr, 0,
+                       Rf_mkCharLen((char*)digits->data, 
(int)digits->size_bytes));
+        result[dst->offset + i] = Rf_asReal(decimal_as_chr);
+      }
+      UNPROTECT(2);
+      return NANOARROW_OK;
+    }
+
     default:
       return EINVAL;
   }
diff --git a/r/src/schema.c b/r/src/schema.c
index a160d8fe..d3987fa4 100644
--- a/r/src/schema.c
+++ b/r/src/schema.c
@@ -289,7 +289,9 @@ SEXP nanoarrow_c_schema_parse(SEXP schema_xptr) {
     SET_VECTOR_ELT(result, 4, Rf_ScalarInteger(schema_view.fixed_size));
   }
 
-  if (schema_view.type == NANOARROW_TYPE_DECIMAL128 ||
+  if (schema_view.type == NANOARROW_TYPE_DECIMAL32 ||
+      schema_view.type == NANOARROW_TYPE_DECIMAL64 ||
+      schema_view.type == NANOARROW_TYPE_DECIMAL128 ||
       schema_view.type == NANOARROW_TYPE_DECIMAL256) {
     SET_VECTOR_ELT(result, 5, Rf_ScalarInteger(schema_view.decimal_bitwidth));
     SET_VECTOR_ELT(result, 6, Rf_ScalarInteger(schema_view.decimal_precision));
diff --git a/r/tests/testthat/_snaps/buffer.md 
b/r/tests/testthat/_snaps/buffer.md
index a3fcface..3f704d32 100644
--- a/r/tests/testthat/_snaps/buffer.md
+++ b/r/tests/testthat/_snaps/buffer.md
@@ -1,3 +1,31 @@
+# buffer printer works for decimal buffer types
+
+    Code
+      str(dec32_array$buffers[[2]])
+    Output
+      <nanoarrow_buffer data<decimal32>[21][84 b]> `-100 -90 -80 -70 -60 -50 
-40 -...`
+
+---
+
+    Code
+      str(dec64_array$buffers[[2]])
+    Output
+      <nanoarrow_buffer data<decimal64>[21][168 b]> `-100 -90 -80 -70 -60 -50 
-40 ...`
+
+---
+
+    Code
+      str(dec128_array$buffers[[2]])
+    Output
+      <nanoarrow_buffer data<decimal128>[21][336 b]> `-100 -90 -80 -70 -60 -50 
-40...`
+
+---
+
+    Code
+      str(dec256_array$buffers[[2]])
+    Output
+      <nanoarrow_buffer data<decimal256>[21][672 b]> `-100 -90 -80 -70 -60 -50 
-40...`
+
 # buffers can be printed
 
     Code
diff --git a/r/tests/testthat/test-altrep.R b/r/tests/testthat/test-altrep.R
index 0658c764..3e138db4 100644
--- a/r/tests/testthat/test-altrep.R
+++ b/r/tests/testthat/test-altrep.R
@@ -52,7 +52,6 @@ test_that("nanoarrow_altrep_chr() works for string", {
 })
 
 test_that("nanoarrow_altrep_chr() works for large string", {
-  skip_if_not_installed("arrow")
   x <- as_nanoarrow_array(letters, schema = na_large_string())
   x_altrep <- nanoarrow_altrep_chr(x)
   expect_identical(x_altrep, letters)
diff --git a/r/tests/testthat/test-as-array.R b/r/tests/testthat/test-as-array.R
index d0290d12..79781721 100644
--- a/r/tests/testthat/test-as-array.R
+++ b/r/tests/testthat/test-as-array.R
@@ -116,6 +116,23 @@ test_that("as_nanoarrow_array() works for integer -> 
na_int64()", {
   expect_identical(convert_array(casted), as.double(1:10))
 })
 
+test_that("as_nanoarrow_array() works for integer -> na_decimal_xxx()", {
+  numbers <- c(1234L, 56L, NA, -10:10)
+  schemas <- list(
+    na_decimal32(9, 3),
+    na_decimal64(9, 3),
+    na_decimal128(9, 3),
+    na_decimal256(9, 3)
+  )
+
+  for (schema in schemas) {
+    array <- as_nanoarrow_array(numbers, schema = schema)
+    actual_schema <- infer_nanoarrow_schema(array)
+    expect_true(nanoarrow_schema_identical(actual_schema, schema))
+    expect_identical(convert_array(array), as.double(numbers))
+  }
+})
+
 test_that("as_nanoarrow_array() works for double() -> na_double()", {
   # Without nulls
   array <- as_nanoarrow_array(as.double(1:10))
@@ -236,6 +253,27 @@ test_that("as_nanoarrow_array() works for double() -> 
na_half_float()", {
   expect_identical(convert_array(array), c(1:10, NA_real_))
 })
 
+
+test_that("as_nanoarrow_array() works for double() -> na_decimal128()", {
+  # Tricky to check the actual buffers here, so just check against arrow
+  skip_if_not_installed("arrow")
+
+  numbers <- round(c(pi, -pi, 123.4567, NA, -123.4567, 0, 123), 4)
+  numbers_neg_scale <- c(12300, -12300, 0, NA, 100)
+
+  array4 <- as_nanoarrow_array(numbers, schema = na_decimal128(9, 4))
+  array5 <- as_nanoarrow_array(numbers, schema = na_decimal128(9, 5))
+  array_neg2 <- as_nanoarrow_array(numbers_neg_scale, schema = 
na_decimal128(9, -2))
+
+  arrow_array4 <- arrow::as_arrow_array(numbers, type = arrow::decimal128(9, 
4))
+  arrow_array5 <- arrow::as_arrow_array(numbers, type = arrow::decimal128(9, 
5))
+  arrow_array_neg2 <- arrow::as_arrow_array(numbers_neg_scale, type = 
arrow::decimal128(9, -2))
+
+  expect_true(arrow_array4$Equals(arrow::as_arrow_array(array4)))
+  expect_true(arrow_array5$Equals(arrow::as_arrow_array(array5)))
+  expect_true(arrow_array_neg2$Equals(arrow::as_arrow_array(array_neg2)))
+})
+
 test_that("as_nanoarrow_array() works for integer64() -> na_int32()", {
   skip_if_not_installed("bit64")
 
@@ -862,3 +900,65 @@ test_that("as_nanoarrow_array() for union type errors for 
unsupported objects",
     "Can't convert data frame with 0 columns"
   )
 })
+
+test_that("storage_integer_for_decimal generates the correct string output", {
+  numbers <- c(
+    0, 1.23, 4, -1/3, .Machine$double.eps,
+    123.4567890, -123.4567890, NA
+  )
+
+  expect_identical(
+    storage_decimal_for_decimal(numbers, 4),
+    c(
+      "0.0000", "1.2300", "4.0000", "-0.3333", "0.0000",
+      "123.4568", "-123.4568", NA
+    )
+  )
+
+  expect_identical(
+    storage_decimal_for_decimal(numbers, 0),
+    c(
+      "0", "1", "4", "0", "0",
+      "123", "-123", NA
+    )
+  )
+
+  expect_identical(
+    storage_decimal_for_decimal(numbers, -1),
+    c(
+      "0", "0", "0", "0", "0",
+      "12", "-12", NA
+    )
+  )
+
+  expect_identical(
+    storage_integer_for_decimal(numbers, 4),
+    c(
+      "00000", "12300", "40000", "-03333", "00000",
+      "1234568", "-1234568", NA
+    )
+  )
+
+  # Check that we're generating the output we think we're generating
+  # with a random sample of numbers at full precision and a random sample
+  # of numbers at low precision.
+  withr::with_seed(4958, {
+    numbers <- runif(1000, -1000, 1000)
+    for (scale in 1:15) {
+      expect_match(
+        storage_decimal_for_decimal(numbers, scale),
+        paste0("-?[0-9]+\\.[0-9]{", scale, "}")
+      )
+    }
+
+    # Also check that we have the required number of digits after the decimal
+    # when the numbers start out not having no digits after the decimal
+    numbers <- round(numbers)
+    for (scale in 1:15) {
+      expect_match(
+        storage_decimal_for_decimal(numbers, scale),
+        paste0("-?[0-9]+\\.[0-9]{", scale, "}")
+      )
+    }
+  })
+})
diff --git a/r/tests/testthat/test-buffer.R b/r/tests/testthat/test-buffer.R
index 3a90928f..6140071a 100644
--- a/r/tests/testthat/test-buffer.R
+++ b/r/tests/testthat/test-buffer.R
@@ -52,6 +52,41 @@ test_that("as_nanoarrow_buffer() works for R atomic types", {
   expect_identical(convert_buffer(buffer_chr), "1234")
 })
 
+test_that("as_nanoarrow_array() works for decimal buffer types", {
+  # Decimal buffers get converted at max precision and zero scale because we
+  # don't have access to the type. This best reflects the logical value of each
+  # buffer item for printing (an xx bit integer).
+  dec32_array <- as_nanoarrow_array(-10:10, schema = na_decimal32(9, 1))
+  dec32_buffer_array <- as_nanoarrow_array(dec32_array$buffers[[2]])
+  expect_identical(infer_nanoarrow_schema(dec32_buffer_array)$format, 
"d:9,0,32")
+
+  dec64_array <- as_nanoarrow_array(-10:10, schema = na_decimal64(9, 1))
+  dec64_buffer_array <- as_nanoarrow_array(dec64_array$buffers[[2]])
+  expect_identical(infer_nanoarrow_schema(dec64_buffer_array)$format, 
"d:18,0,64")
+
+  dec128_array <- as_nanoarrow_array(-10:10, schema = na_decimal128(9, 1))
+  dec128_buffer_array <- as_nanoarrow_array(dec128_array$buffers[[2]])
+  expect_identical(infer_nanoarrow_schema(dec128_buffer_array)$format, 
"d:38,0")
+
+  dec256_array <- as_nanoarrow_array(-10:10, schema = na_decimal256(9, 1))
+  dec256_buffer_array <- as_nanoarrow_array(dec256_array$buffers[[2]])
+  expect_identical(infer_nanoarrow_schema(dec256_buffer_array)$format, 
"d:76,0,256")
+})
+
+test_that("buffer printer works for decimal buffer types", {
+  dec32_array <- as_nanoarrow_array(-10:10, schema = na_decimal32(9, 1))
+  expect_snapshot(str(dec32_array$buffers[[2]]))
+
+  dec64_array <- as_nanoarrow_array(-10:10, schema = na_decimal64(9, 1))
+  expect_snapshot(str(dec64_array$buffers[[2]]))
+
+  dec128_array <- as_nanoarrow_array(-10:10, schema = na_decimal128(9, 1))
+  expect_snapshot(str(dec128_array$buffers[[2]]))
+
+  dec256_array <- as_nanoarrow_array(-10:10, schema = na_decimal256(9, 1))
+  expect_snapshot(str(dec256_array$buffers[[2]]))
+})
+
 test_that("buffers can be printed", {
   expect_snapshot(str(as_nanoarrow_buffer(1:10)))
   expect_snapshot(str(as_nanoarrow_buffer(1:10000)))
diff --git a/r/tests/testthat/test-convert-array.R 
b/r/tests/testthat/test-convert-array.R
index c02f5c92..0357425e 100644
--- a/r/tests/testthat/test-convert-array.R
+++ b/r/tests/testthat/test-convert-array.R
@@ -613,22 +613,31 @@ test_that("convert to vector works for valid double()", {
   )
 })
 
-test_that("convert to vector works for decimal128 -> double()", {
-  skip_if_not_installed("arrow")
-
-  array <- 
as_nanoarrow_array(arrow::Array$create(1:10)$cast(arrow::decimal128(20, 10)))
+test_that("convert to vector works for decimal -> double()", {
+  constructors <- list(na_decimal32, na_decimal64, na_decimal256, 
na_decimal128)
+  for (constructor in constructors) {
+    numbers <- round(c(pi, -pi, 123.4567, NA, -123.4567, 0, 123), 4)
+
+    # Check scale of 4 (min required for lossless roundtrip)
+    array4 <- as_nanoarrow_array(numbers, schema = constructor(9, 4))
+    expect_identical(convert_array(array4), numbers)
+
+    # Check scale of 5 (requires adding some zeroes in C)
+    array5 <- as_nanoarrow_array(numbers, schema = constructor(9, 5))
+    expect_identical(convert_array(array5), numbers)
+
+    # Check negative scale (also requires adding some zeroes in C)
+    numbers_neg_scale <- c(12300, -12300, 0, NA, 100)
+    array_neg2 <- as_nanoarrow_array(numbers_neg_scale, schema = 
constructor(9, -2))
+    expect_identical(convert_array(array_neg2), numbers_neg_scale)
+  }
 
-  # Check via S3 dispatch
-  expect_equal(
-    convert_array(array, double()),
-    as.double(1:10)
-  )
+  # Make sure we agree with arrow
+  skip_if_not_installed("arrow")
 
-  # ...and via C -> S3 dispatch
-  expect_equal(
-    convert_array.default(array, double()),
-    as.double(1:10)
-  )
+  expect_identical(as.vector(arrow::as_arrow_array(array4)), numbers)
+  expect_identical(as.vector(arrow::as_arrow_array(array5)), numbers)
+  expect_identical(as.vector(arrow::as_arrow_array(array_neg2)), 
numbers_neg_scale)
 })
 
 test_that("convert to vector works for null -> double()", {
@@ -872,6 +881,37 @@ test_that("convert to vector works for dictionary<string> 
-> factor()", {
   )
 })
 
+test_that("convert to vector works for decimal -> character()", {
+  constructors <- list(na_decimal32, na_decimal64, na_decimal256, 
na_decimal128)
+  for (constructor in constructors) {
+    numbers <- round(c(pi, -pi, 123.4567, NA, -123.4567, 0, 123), 4)
+
+    # Check scale of 4 (min required for lossless roundtrip)
+    array4 <- as_nanoarrow_array(numbers, schema = constructor(9, 4))
+    expect_identical(
+      convert_array(array4, character()),
+      c("3.1416", "-3.1416", "123.4567", NA_character_,
+        "-123.4567", "0.0000", "123.0000")
+    )
+
+    # Check scale of 5 (requires adding some zeroes in C)
+    array5 <- as_nanoarrow_array(numbers, schema = constructor(9, 5))
+    expect_identical(
+      convert_array(array5, character()),
+      c("3.14160", "-3.14160", "123.45670", NA_character_,
+        "-123.45670", "0.00000", "123.00000")
+    )
+
+    # Check negative scale (also requires adding some zeroes in C)
+    numbers_neg_scale <- c(12300, -12300, 0, NA, 100)
+    array_neg2 <- as_nanoarrow_array(numbers_neg_scale, schema = 
constructor(9, -2))
+    expect_identical(
+      convert_array(array_neg2, character()),
+      c("12300", "-12300", "000", NA_character_, "100")
+    )
+  }
+})
+
 test_that("batched convert to vector works for dictionary<string> -> 
factor()", {
   # A slightly different path: convert_array.factor() called from C multiple
   # times with different dictionaries each time.
diff --git a/r/tests/testthat/test-infer-ptype.R 
b/r/tests/testthat/test-infer-ptype.R
index ead65359..6e8f1193 100644
--- a/r/tests/testthat/test-infer-ptype.R
+++ b/r/tests/testthat/test-infer-ptype.R
@@ -125,17 +125,17 @@ test_that("infer_nanoarrow_ptype() infers ptypes for 
nested types", {
 })
 
 test_that("infer_nanoarrow_ptype() errors for types it can't infer",  {
-  unsupported_array <- nanoarrow_array_init(na_decimal256(3, 4))
+  unsupported_array <- nanoarrow_array_init(na_list_view(na_int32()))
   expect_error(
     infer_nanoarrow_ptype(as_nanoarrow_array(unsupported_array)),
-    "Can't infer R vector type for <decimal256\\(3, 4\\)>"
+    "Can't infer R vector type for <list_view<item: int32>>"
   )
 
   unsupported_struct <- nanoarrow_array_init(
-    na_struct(list(col = na_decimal256(3, 4)))
+    na_struct(list(col = na_list_view(na_int32())))
   )
   expect_error(
     infer_nanoarrow_ptype(as_nanoarrow_array(unsupported_struct)),
-    "Can't infer R vector type for `col` <decimal256\\(3, 4\\)>"
+    "Can't infer R vector type for `col` <list_view<item: int32>>"
   )
 })
diff --git a/r/tests/testthat/test-type.R b/r/tests/testthat/test-type.R
index eb10fac6..018421ea 100644
--- a/r/tests/testthat/test-type.R
+++ b/r/tests/testthat/test-type.R
@@ -74,7 +74,18 @@ test_that("timestamp type passes along timezone parameter", {
 })
 
 test_that("decimal types pass along precision and scale", {
+  schema <- na_decimal32(9, 3)
+  expect_identical(nanoarrow_schema_parse(schema)$decimal_bitwidth, 32L)
+  expect_identical(nanoarrow_schema_parse(schema)$decimal_precision, 9L)
+  expect_identical(nanoarrow_schema_parse(schema)$decimal_scale, 3L)
+
+  schema <- na_decimal64(12, 10)
+  expect_identical(nanoarrow_schema_parse(schema)$decimal_bitwidth, 64L)
+  expect_identical(nanoarrow_schema_parse(schema)$decimal_precision, 12L)
+  expect_identical(nanoarrow_schema_parse(schema)$decimal_scale, 10L)
+
   schema <- na_decimal128(12, 10)
+  expect_identical(nanoarrow_schema_parse(schema)$decimal_bitwidth, 128L)
   expect_identical(nanoarrow_schema_parse(schema)$decimal_precision, 12L)
   expect_identical(nanoarrow_schema_parse(schema)$decimal_scale, 10L)
 
@@ -127,6 +138,18 @@ test_that("list constructors assign the correct child 
type", {
   expect_identical(schema$children[[1]]$format, "i")
 })
 
+test_that("list_view constructors assign the correct child type", {
+  schema <- na_list_view(na_int32())
+  expect_identical(schema$format, "+vl")
+  expect_named(schema$children, "item")
+  expect_identical(schema$children[[1]]$format, "i")
+
+  schema <- na_large_list_view(na_int32())
+  expect_identical(schema$format, "+vL")
+  expect_named(schema$children, "item")
+  expect_identical(schema$children[[1]]$format, "i")
+})
+
 test_that("map constructor assigns the correct key and value types", {
   schema <- na_map(na_int32(nullable = FALSE), na_int64())
   expect_named(schema$children, "entries")
diff --git a/src/nanoarrow/common/schema.c b/src/nanoarrow/common/schema.c
index b6cf1b00..2722793e 100644
--- a/src/nanoarrow/common/schema.c
+++ b/src/nanoarrow/common/schema.c
@@ -278,18 +278,34 @@ ArrowErrorCode ArrowSchemaSetTypeDecimal(struct 
ArrowSchema* schema, enum ArrowT
   int n_chars;
   switch (type) {
     case NANOARROW_TYPE_DECIMAL32:
+      if (decimal_precision > 9) {
+        return EINVAL;
+      }
+
       n_chars = snprintf(buffer, sizeof(buffer), "d:%d,%d,32", 
decimal_precision,
                          decimal_scale);
       break;
     case NANOARROW_TYPE_DECIMAL64:
+      if (decimal_precision > 18) {
+        return EINVAL;
+      }
+
       n_chars = snprintf(buffer, sizeof(buffer), "d:%d,%d,64", 
decimal_precision,
                          decimal_scale);
       break;
     case NANOARROW_TYPE_DECIMAL128:
+      if (decimal_precision > 38) {
+        return EINVAL;
+      }
+
       n_chars =
           snprintf(buffer, sizeof(buffer), "d:%d,%d", decimal_precision, 
decimal_scale);
       break;
     case NANOARROW_TYPE_DECIMAL256:
+      if (decimal_precision > 76) {
+        return EINVAL;
+      }
+
       n_chars = snprintf(buffer, sizeof(buffer), "d:%d,%d,256", 
decimal_precision,
                          decimal_scale);
       break;
diff --git a/src/nanoarrow/common/schema_test.cc 
b/src/nanoarrow/common/schema_test.cc
index 3a12a0b3..873d1ca0 100644
--- a/src/nanoarrow/common/schema_test.cc
+++ b/src/nanoarrow/common/schema_test.cc
@@ -265,48 +265,64 @@ TEST(SchemaTest, SchemaInitDecimal) {
   EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL128, -1, 
1), EINVAL);
   EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DOUBLE, 1, 2), 
EINVAL);
 
-  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL128, 1, 
2),
+  ArrowSchemaInit(&schema);
+  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL32, 10, 
3), EINVAL);
+  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL32, 9, 3),
             NANOARROW_OK);
-  EXPECT_STREQ(schema.format, "d:1,2");
+  EXPECT_STREQ(schema.format, "d:9,3,32");
+  ArrowSchemaRelease(&schema);
+
+  ArrowSchemaInit(&schema);
+  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL64, 19, 
3), EINVAL);
+  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL64, 9, 3),
+            NANOARROW_OK);
+  EXPECT_STREQ(schema.format, "d:9,3,64");
+  ArrowSchemaRelease(&schema);
+
+  ArrowSchemaInit(&schema);
+  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL128, 39, 
3), EINVAL);
+  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL128, 9, 
3),
+            NANOARROW_OK);
+  EXPECT_STREQ(schema.format, "d:9,3");
+  ArrowSchemaRelease(&schema);
+
+  ArrowSchemaInit(&schema);
+  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL32, 77, 
3), EINVAL);
+  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL256, 9, 
3),
+            NANOARROW_OK);
+  EXPECT_STREQ(schema.format, "d:9,3,256");
+  ArrowSchemaRelease(&schema);
 
 #if defined(NANOARROW_BUILD_TESTS_WITH_ARROW)
+  ArrowSchemaInit(&schema);
+  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL128, 9, 
3),
+            NANOARROW_OK);
   auto arrow_type = ImportType(&schema);
   ARROW_EXPECT_OK(arrow_type);
-  EXPECT_TRUE(arrow_type.ValueUnsafe()->Equals(decimal128(1, 2)));
+  EXPECT_TRUE(arrow_type.ValueUnsafe()->Equals(decimal128(9, 3)));
 
   ArrowSchemaInit(&schema);
-  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL256, 3, 
4),
+  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL256, 9, 
3),
             NANOARROW_OK);
-  EXPECT_STREQ(schema.format, "d:3,4,256");
   arrow_type = ImportType(&schema);
   ARROW_EXPECT_OK(arrow_type);
-  EXPECT_TRUE(arrow_type.ValueUnsafe()->Equals(decimal256(3, 4)));
+  EXPECT_TRUE(arrow_type.ValueUnsafe()->Equals(decimal256(9, 3)));
 
+#if ARROW_VERSION_MAJOR >= 18
   ArrowSchemaInit(&schema);
-  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL32, 3, 4),
+  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL32, 9, 3),
             NANOARROW_OK);
-  EXPECT_STREQ(schema.format, "d:3,4,32");
-#if ARROW_MAJOR_VERSION >= 18
   arrow_type = ImportType(&schema);
   ARROW_EXPECT_OK(arrow_type);
-  EXPECT_TRUE(arrow_type.ValueUnsafe()->Equals(decimal32(3, 4)));
-#else
-  ArrowSchemaRelease(&schema);
-#endif
+  EXPECT_TRUE(arrow_type.ValueUnsafe()->Equals(decimal32(9, 3)));
 
   ArrowSchemaInit(&schema);
-  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL64, 3, 4),
+  EXPECT_EQ(ArrowSchemaSetTypeDecimal(&schema, NANOARROW_TYPE_DECIMAL64, 9, 3),
             NANOARROW_OK);
-  EXPECT_STREQ(schema.format, "d:3,4,64");
-#if ARROW_MAJOR_VERSION >= 18
   arrow_type = ImportType(&schema);
   ARROW_EXPECT_OK(arrow_type);
-  EXPECT_TRUE(arrow_type.ValueUnsafe()->Equals(decimal64(3, 4)));
-#else
-  ArrowSchemaRelease(&schema);
+  EXPECT_TRUE(arrow_type.ValueUnsafe()->Equals(decimal64(9, 3)));
 #endif
-#else
-  ArrowSchemaRelease(&schema);
 #endif
 }
 


Reply via email to