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

kazuyukitanimura pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-comet.git


The following commit(s) were added to refs/heads/main by this push:
     new 64b5f3c6 feat: Spark-4.0 widening type support (#604)
64b5f3c6 is described below

commit 64b5f3c6e1a8c6e8ebda6d1ec75819593d3cacd1
Author: KAZUYUKI TANIMURA <[email protected]>
AuthorDate: Thu Jul 18 12:38:58 2024 -0700

    feat: Spark-4.0 widening type support (#604)
    
    ## Rationale for this change
    
    To be ready for Spark 4.0
    
    ## What changes are included in this PR?
    
    This PR adds type widening feature support introduced in Spark-4.0
    
    ## How are these changes tested?
    
    Enabled ParquetTypeWideningSuite
---
 .../main/java/org/apache/comet/parquet/Native.java |   1 +
 .../java/org/apache/comet/parquet/TypeUtil.java    |  43 +++-
 .../main/java/org/apache/comet/parquet/Utils.java  |  15 +-
 dev/diffs/4.0.0-preview1.diff                      |  49 ++--
 native/core/benches/parquet_read.rs                |   4 +-
 native/core/src/parquet/data_type.rs               |   5 +
 native/core/src/parquet/mod.rs                     |   9 +-
 native/core/src/parquet/read/column.rs             | 176 ++++++++++++-
 native/core/src/parquet/read/levels.rs             |   4 +-
 native/core/src/parquet/read/values.rs             | 282 ++++++++++++++++-----
 native/core/src/parquet/util/jni.rs                |  12 +-
 11 files changed, 484 insertions(+), 116 deletions(-)

diff --git a/common/src/main/java/org/apache/comet/parquet/Native.java 
b/common/src/main/java/org/apache/comet/parquet/Native.java
index b40e27e7..f4820fed 100644
--- a/common/src/main/java/org/apache/comet/parquet/Native.java
+++ b/common/src/main/java/org/apache/comet/parquet/Native.java
@@ -70,6 +70,7 @@ public final class Native extends NativeBase {
       int maxDl,
       int maxRl,
       int bitWidth,
+      int expectedBitWidth,
       boolean isSigned,
       int typeLength,
       int precision,
diff --git a/common/src/main/java/org/apache/comet/parquet/TypeUtil.java 
b/common/src/main/java/org/apache/comet/parquet/TypeUtil.java
index bfbb7d0d..0ec5042c 100644
--- a/common/src/main/java/org/apache/comet/parquet/TypeUtil.java
+++ b/common/src/main/java/org/apache/comet/parquet/TypeUtil.java
@@ -29,6 +29,7 @@ import org.apache.parquet.schema.Type;
 import org.apache.parquet.schema.Types;
 import org.apache.spark.package$;
 import 
org.apache.spark.sql.execution.datasources.SchemaColumnConvertNotSupportedException;
+import org.apache.spark.sql.internal.SQLConf;
 import org.apache.spark.sql.types.*;
 
 import org.apache.comet.CometConf;
@@ -57,7 +58,9 @@ public class TypeUtil {
     if (type == DataTypes.BooleanType || type == DataTypes.NullType) {
       builder = Types.primitive(PrimitiveType.PrimitiveTypeName.BOOLEAN, 
repetition);
     } else if (type == DataTypes.IntegerType || type instanceof 
YearMonthIntervalType) {
-      builder = Types.primitive(PrimitiveType.PrimitiveTypeName.INT32, 
repetition);
+      builder =
+          Types.primitive(PrimitiveType.PrimitiveTypeName.INT32, repetition)
+              .as(LogicalTypeAnnotation.intType(32, true));
     } else if (type == DataTypes.DateType) {
       builder =
           Types.primitive(PrimitiveType.PrimitiveTypeName.INT32, repetition)
@@ -148,6 +151,12 @@ public class TypeUtil {
           return;
         } else if (sparkType instanceof YearMonthIntervalType) {
           return;
+        } else if (sparkType == DataTypes.DoubleType && isSpark40Plus()) {
+          return;
+        } else if (sparkType == TimestampNTZType$.MODULE$
+            && isSpark40Plus()
+            && logicalTypeAnnotation instanceof DateLogicalTypeAnnotation) {
+          return;
         }
         break;
       case INT64:
@@ -159,11 +168,13 @@ public class TypeUtil {
           // For unsigned int64, it stores as plain signed int64 in Parquet 
when dictionary
           // fallbacks. We read them as decimal values.
           return;
-        } else if (isTimestampTypeMatched(logicalTypeAnnotation, 
TimeUnit.MICROS)) {
+        } else if (isTimestampTypeMatched(logicalTypeAnnotation, 
TimeUnit.MICROS)
+            && (sparkType == TimestampNTZType$.MODULE$ || sparkType == 
DataTypes.TimestampType)) {
           validateTimestampType(logicalTypeAnnotation, sparkType);
           // TODO: use dateTimeRebaseMode from Spark side
           return;
-        } else if (isTimestampTypeMatched(logicalTypeAnnotation, 
TimeUnit.MILLIS)) {
+        } else if (isTimestampTypeMatched(logicalTypeAnnotation, 
TimeUnit.MILLIS)
+            && (sparkType == TimestampNTZType$.MODULE$ || sparkType == 
DataTypes.TimestampType)) {
           validateTimestampType(logicalTypeAnnotation, sparkType);
           return;
         }
@@ -266,9 +277,29 @@ public class TypeUtil {
       DecimalLogicalTypeAnnotation decimalType = 
(DecimalLogicalTypeAnnotation) typeAnnotation;
       // It's OK if the required decimal precision is larger than or equal to 
the physical decimal
       // precision in the Parquet metadata, as long as the decimal scale is 
the same.
-      return decimalType.getPrecision() <= d.precision()
-          && (decimalType.getScale() == d.scale()
-              || (isSpark40Plus() && decimalType.getScale() <= d.scale()));
+      return (decimalType.getPrecision() <= d.precision() && 
decimalType.getScale() == d.scale())
+          || (isSpark40Plus()
+              && (!SQLConf.get().parquetVectorizedReaderEnabled()
+                  || (decimalType.getScale() <= d.scale()
+                      && decimalType.getPrecision() - decimalType.getScale()
+                          <= d.precision() - d.scale())));
+    } else if (isSpark40Plus()) {
+      boolean isNullTypeAnnotation = typeAnnotation == null;
+      boolean isIntTypeAnnotation = typeAnnotation instanceof 
IntLogicalTypeAnnotation;
+      if (!SQLConf.get().parquetVectorizedReaderEnabled()) {
+        return isNullTypeAnnotation || isIntTypeAnnotation;
+      } else if (isNullTypeAnnotation
+          || (isIntTypeAnnotation && ((IntLogicalTypeAnnotation) 
typeAnnotation).isSigned())) {
+        PrimitiveType.PrimitiveTypeName typeName =
+            descriptor.getPrimitiveType().getPrimitiveTypeName();
+        int integerPrecision = d.precision() - d.scale();
+        switch (typeName) {
+          case INT32:
+            return integerPrecision >= 
DecimalType$.MODULE$.IntDecimal().precision();
+          case INT64:
+            return integerPrecision >= 
DecimalType$.MODULE$.LongDecimal().precision();
+        }
+      }
     }
     return false;
   }
diff --git a/common/src/main/java/org/apache/comet/parquet/Utils.java 
b/common/src/main/java/org/apache/comet/parquet/Utils.java
index 2d4b83a6..f73251e2 100644
--- a/common/src/main/java/org/apache/comet/parquet/Utils.java
+++ b/common/src/main/java/org/apache/comet/parquet/Utils.java
@@ -115,7 +115,7 @@ public class Utils {
       promotionInfo = new TypePromotionInfo(readType);
     } else {
       // If type promotion is not enable, we'll just use the Parquet primitive 
type and precision.
-      promotionInfo = new TypePromotionInfo(primitiveTypeId, precision, scale);
+      promotionInfo = new TypePromotionInfo(primitiveTypeId, precision, scale, 
bitWidth);
     }
 
     return Native.initColumnReader(
@@ -126,6 +126,7 @@ public class Utils {
         descriptor.getMaxDefinitionLevel(),
         descriptor.getMaxRepetitionLevel(),
         bitWidth,
+        promotionInfo.bitWidth,
         isSigned,
         primitiveType.getTypeLength(),
         precision,
@@ -147,11 +148,14 @@ public class Utils {
     int precision;
     // Decimal scale from the Spark read schema, or -1 if it's not decimal 
type.
     int scale;
+    // Integer bit width from the Spark read schema, or -1 if it's not integer 
type.
+    int bitWidth;
 
-    TypePromotionInfo(int physicalTypeId, int precision, int scale) {
+    TypePromotionInfo(int physicalTypeId, int precision, int scale, int 
bitWidth) {
       this.physicalTypeId = physicalTypeId;
       this.precision = precision;
       this.scale = scale;
+      this.bitWidth = bitWidth;
     }
 
     TypePromotionInfo(DataType sparkReadType) {
@@ -164,15 +168,22 @@ public class Utils {
       LogicalTypeAnnotation annotation = 
primitiveType.getLogicalTypeAnnotation();
       int precision = -1;
       int scale = -1;
+      int bitWidth = -1;
       if (annotation instanceof 
LogicalTypeAnnotation.DecimalLogicalTypeAnnotation) {
         LogicalTypeAnnotation.DecimalLogicalTypeAnnotation decimalAnnotation =
             (LogicalTypeAnnotation.DecimalLogicalTypeAnnotation) annotation;
         precision = decimalAnnotation.getPrecision();
         scale = decimalAnnotation.getScale();
       }
+      if (annotation instanceof 
LogicalTypeAnnotation.IntLogicalTypeAnnotation) {
+        LogicalTypeAnnotation.IntLogicalTypeAnnotation intAnnotation =
+            (LogicalTypeAnnotation.IntLogicalTypeAnnotation) annotation;
+        bitWidth = intAnnotation.getBitWidth();
+      }
       this.physicalTypeId = physicalTypeId;
       this.precision = precision;
       this.scale = scale;
+      this.bitWidth = bitWidth;
     }
   }
 
diff --git a/dev/diffs/4.0.0-preview1.diff b/dev/diffs/4.0.0-preview1.diff
index abd6e9a0..dfd57ce8 100644
--- a/dev/diffs/4.0.0-preview1.diff
+++ b/dev/diffs/4.0.0-preview1.diff
@@ -2012,28 +2012,39 @@ index 25f6af1cc33..37b40cb5524 100644
        val e = testSchemaMismatch(dir.getCanonicalPath, 
vectorizedReaderEnabled = false)
        val expectedMessage = "Encountered error while reading file"
 diff --git 
a/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetTypeWideningSuite.scala
 
b/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetTypeWideningSuite.scala
-index 4bd35e0789b..6bfedb65078 100644
+index 4bd35e0789b..6544d86dbe0 100644
 --- 
a/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetTypeWideningSuite.scala
 +++ 
b/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetTypeWideningSuite.scala
-@@ -24,7 +24,7 @@ import 
org.apache.parquet.format.converter.ParquetMetadataConverter
- import org.apache.parquet.hadoop.{ParquetFileReader, ParquetOutputFormat}
- 
- import org.apache.spark.SparkException
--import org.apache.spark.sql.{DataFrame, QueryTest, Row}
-+import org.apache.spark.sql.{DataFrame, IgnoreCometSuite, QueryTest, Row}
- import org.apache.spark.sql.execution.adaptive.AdaptiveSparkPlanHelper
- import 
org.apache.spark.sql.execution.datasources.SchemaColumnConvertNotSupportedException
- import org.apache.spark.sql.functions.col
-@@ -38,7 +38,8 @@ class ParquetTypeWideningSuite
-     extends QueryTest
-     with ParquetTest
-     with SharedSparkSession
--    with AdaptiveSparkPlanHelper {
-+    with AdaptiveSparkPlanHelper
-+    with IgnoreCometSuite { // TODO: 
https://github.com/apache/datafusion-comet/issues/551
- 
-   import testImplicits._
+@@ -65,7 +65,9 @@ class ParquetTypeWideningSuite
+     withClue(
+       s"with dictionary encoding '$dictionaryEnabled' with timestamp rebase 
mode " +
+         s"'$timestampRebaseMode''") {
+-      withAllParquetWriters {
++      // TODO: Comet cannot read DELTA_BINARY_PACKED created by V2 writer
++      // https://github.com/apache/datafusion-comet/issues/574
++      // withAllParquetWriters {
+         withTempDir { dir =>
+           val expected =
+             writeParquetFiles(dir, values, fromType, dictionaryEnabled, 
timestampRebaseMode)
+@@ -86,7 +88,7 @@ class ParquetTypeWideningSuite
+             }
+           }
+         }
+-      }
++      // }
+     }
+   }
  
+@@ -190,7 +192,8 @@ class ParquetTypeWideningSuite
+       (Seq("1", "2", Short.MinValue.toString), ShortType, DoubleType),
+       (Seq("1", "2", Int.MinValue.toString), IntegerType, DoubleType),
+       (Seq("1.23", "10.34"), FloatType, DoubleType),
+-      (Seq("2020-01-01", "2020-01-02", "1312-02-27"), DateType, 
TimestampNTZType)
++      // TODO: Comet cannot handle older than "1582-10-15"
++      (Seq("2020-01-01", "2020-01-02"/* , "1312-02-27" */), DateType, 
TimestampNTZType)
+     )
+   }
+   test(s"parquet widening conversion $fromType -> $toType") {
 diff --git 
a/sql/core/src/test/scala/org/apache/spark/sql/execution/debug/DebuggingSuite.scala
 
b/sql/core/src/test/scala/org/apache/spark/sql/execution/debug/DebuggingSuite.scala
 index b8f3ea3c6f3..bbd44221288 100644
 --- 
a/sql/core/src/test/scala/org/apache/spark/sql/execution/debug/DebuggingSuite.scala
diff --git a/native/core/benches/parquet_read.rs 
b/native/core/benches/parquet_read.rs
index 32463c07..1f8178cd 100644
--- a/native/core/benches/parquet_read.rs
+++ b/native/core/benches/parquet_read.rs
@@ -54,10 +54,10 @@ fn bench(c: &mut Criterion) {
         );
         b.iter(|| {
             let cd = ColumnDescriptor::new(t.clone(), 0, 0, 
ColumnPath::from(Vec::new()));
-            let promition_info = TypePromotionInfo::new(PhysicalType::INT32, 
-1, -1);
+            let promotion_info = TypePromotionInfo::new(PhysicalType::INT32, 
-1, -1, 32);
             let mut column_reader = TestColumnReader::new(
                 cd,
-                promition_info,
+                promotion_info,
                 BATCH_SIZE,
                 pages.clone(),
                 expected_num_values,
diff --git a/native/core/src/parquet/data_type.rs 
b/native/core/src/parquet/data_type.rs
index 0fc960e9..cd578e2a 100644
--- a/native/core/src/parquet/data_type.rs
+++ b/native/core/src/parquet/data_type.rs
@@ -30,11 +30,15 @@ make_type!(BoolType);
 make_type!(Int8Type);
 make_type!(UInt8Type);
 make_type!(Int16Type);
+make_type!(Int16ToDoubleType);
 make_type!(UInt16Type);
 make_type!(Int32Type);
 make_type!(Int32To64Type);
+make_type!(Int32ToDecimal64Type);
+make_type!(Int32ToDoubleType);
 make_type!(UInt32Type);
 make_type!(Int64Type);
+make_type!(Int64ToDecimal64Type);
 make_type!(UInt64Type);
 make_type!(FloatType);
 make_type!(DoubleType);
@@ -48,6 +52,7 @@ make_type!(FLBADecimal32Type);
 make_type!(FLBADecimal64Type);
 make_type!(FLBAType);
 make_type!(Int32DateType);
+make_type!(Int32TimestampMicrosType);
 make_type!(Int64TimestampMillisType);
 make_type!(Int64TimestampMicrosType);
 make_type!(Int96TimestampMicrosType);
diff --git a/native/core/src/parquet/mod.rs b/native/core/src/parquet/mod.rs
index e6acaa26..c523f843 100644
--- a/native/core/src/parquet/mod.rs
+++ b/native/core/src/parquet/mod.rs
@@ -67,6 +67,7 @@ pub extern "system" fn 
Java_org_apache_comet_parquet_Native_initColumnReader(
     max_dl: jint,
     max_rl: jint,
     bit_width: jint,
+    read_bit_width: jint,
     is_signed: jboolean,
     type_length: jint,
     precision: jint,
@@ -95,8 +96,12 @@ pub extern "system" fn 
Java_org_apache_comet_parquet_Native_initColumnReader(
             is_adjusted_utc,
             jni_path,
         )?;
-        let promotion_info =
-            TypePromotionInfo::new_from_jni(read_primitive_type, 
read_precision, read_scale);
+        let promotion_info = TypePromotionInfo::new_from_jni(
+            read_primitive_type,
+            read_precision,
+            read_scale,
+            read_bit_width,
+        );
         let ctx = Context {
             column_reader: ColumnReader::get(
                 desc,
diff --git a/native/core/src/parquet/read/column.rs 
b/native/core/src/parquet/read/column.rs
index 22bade6b..feb34271 100644
--- a/native/core/src/parquet/read/column.rs
+++ b/native/core/src/parquet/read/column.rs
@@ -51,13 +51,18 @@ pub enum ColumnReader {
     Int8ColumnReader(TypedColumnReader<Int8Type>),
     UInt8ColumnReader(TypedColumnReader<UInt8Type>),
     Int16ColumnReader(TypedColumnReader<Int16Type>),
+    Int16ToDoubleColumnReader(TypedColumnReader<Int16ToDoubleType>),
     UInt16ColumnReader(TypedColumnReader<UInt16Type>),
     Int32ColumnReader(TypedColumnReader<Int32Type>),
     Int32To64ColumnReader(TypedColumnReader<Int32To64Type>),
+    Int32ToDecimal64ColumnReader(TypedColumnReader<Int32ToDecimal64Type>),
+    Int32ToDoubleColumnReader(TypedColumnReader<Int32ToDoubleType>),
     UInt32ColumnReader(TypedColumnReader<UInt32Type>),
     Int32DecimalColumnReader(TypedColumnReader<Int32DecimalType>),
     Int32DateColumnReader(TypedColumnReader<Int32DateType>),
+    
Int32TimestampMicrosColumnReader(TypedColumnReader<Int32TimestampMicrosType>),
     Int64ColumnReader(TypedColumnReader<Int64Type>),
+    Int64ToDecimal64ColumnReader(TypedColumnReader<Int64ToDecimal64Type>),
     UInt64DecimalColumnReader(TypedColumnReader<UInt64Type>),
     Int64DecimalColumnReader(TypedColumnReader<Int64DecimalType>),
     
Int64TimestampMillisColumnReader(TypedColumnReader<Int64TimestampMillisType>),
@@ -124,19 +129,81 @@ impl ColumnReader {
                             bit_width,
                             is_signed,
                         } => match (bit_width, is_signed) {
-                            (8, true) => typed_reader!(Int8ColumnReader, Int8),
+                            (8, true) => match promotion_info.physical_type {
+                                PhysicalType::FIXED_LEN_BYTE_ARRAY => {
+                                    if promotion_info.precision <= 
DECIMAL_MAX_INT_DIGITS
+                                        && promotion_info.scale < 1
+                                    {
+                                        typed_reader!(Int32ColumnReader, Int32)
+                                    } else if promotion_info.precision <= 
DECIMAL_MAX_LONG_DIGITS {
+                                        typed_reader!(
+                                            Int32ToDecimal64ColumnReader,
+                                            ArrowDataType::Decimal128(
+                                                promotion_info.precision as u8,
+                                                promotion_info.scale as i8
+                                            )
+                                        )
+                                    } else {
+                                        typed_reader!(
+                                            Int32DecimalColumnReader,
+                                            ArrowDataType::Decimal128(
+                                                promotion_info.precision as u8,
+                                                promotion_info.scale as i8
+                                            )
+                                        )
+                                    }
+                                }
+                                _ => typed_reader!(Int8ColumnReader, Int8),
+                            },
                             (8, false) => typed_reader!(UInt8ColumnReader, 
Int16),
-                            (16, true) => typed_reader!(Int16ColumnReader, 
Int16),
+                            (16, true) => match promotion_info.physical_type {
+                                PhysicalType::DOUBLE => {
+                                    typed_reader!(Int16ToDoubleColumnReader, 
Float64)
+                                }
+                                PhysicalType::INT32 if 
promotion_info.bit_width == 32 => {
+                                    typed_reader!(Int32ColumnReader, Int32)
+                                }
+                                PhysicalType::FIXED_LEN_BYTE_ARRAY => {
+                                    if promotion_info.precision <= 
DECIMAL_MAX_INT_DIGITS
+                                        && promotion_info.scale < 1
+                                    {
+                                        typed_reader!(Int32ColumnReader, Int32)
+                                    } else if promotion_info.precision <= 
DECIMAL_MAX_LONG_DIGITS {
+                                        typed_reader!(
+                                            Int32ToDecimal64ColumnReader,
+                                            ArrowDataType::Decimal128(
+                                                promotion_info.precision as u8,
+                                                promotion_info.scale as i8
+                                            )
+                                        )
+                                    } else {
+                                        typed_reader!(
+                                            Int32DecimalColumnReader,
+                                            ArrowDataType::Decimal128(
+                                                promotion_info.precision as u8,
+                                                promotion_info.scale as i8
+                                            )
+                                        )
+                                    }
+                                }
+                                _ => typed_reader!(Int16ColumnReader, Int16),
+                            },
                             (16, false) => typed_reader!(UInt16ColumnReader, 
Int32),
-                            (32, true) => typed_reader!(Int32ColumnReader, 
Int32),
+                            (32, true) => match promotion_info.physical_type {
+                                PhysicalType::INT32 if 
promotion_info.bit_width == 16 => {
+                                    typed_reader!(Int16ColumnReader, Int16)
+                                }
+                                _ => typed_reader!(Int32ColumnReader, Int32),
+                            },
                             (32, false) => typed_reader!(UInt32ColumnReader, 
Int64),
                             _ => unimplemented!("Unsupported INT32 annotation: 
{:?}", lt),
                         },
                         LogicalType::Decimal {
-                            scale,
+                            scale: _,
                             precision: _,
                         } => {
-                            if use_decimal_128 || scale < 
&promotion_info.scale {
+                            if use_decimal_128 || promotion_info.precision > 
DECIMAL_MAX_LONG_DIGITS
+                            {
                                 typed_reader!(
                                     Int32DecimalColumnReader,
                                     ArrowDataType::Decimal128(
@@ -145,17 +212,56 @@ impl ColumnReader {
                                     )
                                 )
                             } else {
-                                typed_reader!(Int32ColumnReader, Int32)
+                                typed_reader!(
+                                    Int32ToDecimal64ColumnReader,
+                                    ArrowDataType::Decimal128(
+                                        promotion_info.precision as u8,
+                                        promotion_info.scale as i8
+                                    )
+                                )
                             }
                         }
-                        LogicalType::Date => 
typed_reader!(Int32DateColumnReader, Date32),
+                        LogicalType::Date => match 
promotion_info.physical_type {
+                            PhysicalType::INT64 => typed_reader!(
+                                Int32TimestampMicrosColumnReader,
+                                
ArrowDataType::Timestamp(TimeUnit::Microsecond, None)
+                            ),
+                            _ => typed_reader!(Int32DateColumnReader, Date32),
+                        },
                         lt => unimplemented!("Unsupported logical type for 
INT32: {:?}", lt),
                     }
                 } else {
                     // We support type promotion from int to long
                     match promotion_info.physical_type {
+                        PhysicalType::INT32 if promotion_info.bit_width == 16 
=> {
+                            typed_reader!(Int16ColumnReader, Int16)
+                        }
                         PhysicalType::INT32 => 
typed_reader!(Int32ColumnReader, Int32),
                         PhysicalType::INT64 => 
typed_reader!(Int32To64ColumnReader, Int64),
+                        PhysicalType::DOUBLE => 
typed_reader!(Int32ToDoubleColumnReader, Float64),
+                        PhysicalType::FIXED_LEN_BYTE_ARRAY => {
+                            if promotion_info.precision <= 
DECIMAL_MAX_INT_DIGITS
+                                && promotion_info.scale < 1
+                            {
+                                typed_reader!(Int32ColumnReader, Int32)
+                            } else if promotion_info.precision <= 
DECIMAL_MAX_LONG_DIGITS {
+                                typed_reader!(
+                                    Int32ToDecimal64ColumnReader,
+                                    ArrowDataType::Decimal128(
+                                        promotion_info.precision as u8,
+                                        promotion_info.scale as i8
+                                    )
+                                )
+                            } else {
+                                typed_reader!(
+                                    Int32DecimalColumnReader,
+                                    ArrowDataType::Decimal128(
+                                        promotion_info.precision as u8,
+                                        promotion_info.scale as i8
+                                    )
+                                )
+                            }
+                        }
                         t => unimplemented!("Unsupported read physical type 
for INT32: {}", t),
                     }
                 }
@@ -175,10 +281,11 @@ impl ColumnReader {
                             _ => panic!("Unsupported INT64 annotation: {:?}", 
lt),
                         },
                         LogicalType::Decimal {
-                            scale,
+                            scale: _,
                             precision: _,
                         } => {
-                            if use_decimal_128 || scale < 
&promotion_info.scale {
+                            if use_decimal_128 || promotion_info.precision > 
DECIMAL_MAX_LONG_DIGITS
+                            {
                                 typed_reader!(
                                     Int64DecimalColumnReader,
                                     ArrowDataType::Decimal128(
@@ -187,7 +294,13 @@ impl ColumnReader {
                                     )
                                 )
                             } else {
-                                typed_reader!(Int64ColumnReader, Int64)
+                                typed_reader!(
+                                    Int64ToDecimal64ColumnReader,
+                                    ArrowDataType::Decimal128(
+                                        promotion_info.precision as u8,
+                                        promotion_info.scale as i8
+                                    )
+                                )
                             }
                         }
                         LogicalType::Timestamp {
@@ -226,8 +339,25 @@ impl ColumnReader {
                         lt => panic!("Unsupported logical type for INT64: 
{:?}", lt),
                     }
                 } else {
-                    // By default it is INT(64, true)
-                    typed_reader!(Int64ColumnReader, Int64)
+                    match promotion_info.physical_type {
+                        PhysicalType::FIXED_LEN_BYTE_ARRAY => {
+                            if promotion_info.precision <= 
DECIMAL_MAX_LONG_DIGITS
+                                && promotion_info.scale < 1
+                            {
+                                typed_reader!(Int64ColumnReader, Int64)
+                            } else {
+                                typed_reader!(
+                                    Int64DecimalColumnReader,
+                                    ArrowDataType::Decimal128(
+                                        promotion_info.precision as u8,
+                                        promotion_info.scale as i8
+                                    )
+                                )
+                            }
+                        }
+                        // By default it is INT(64, true)
+                        _ => typed_reader!(Int64ColumnReader, Int64),
+                    }
                 }
             }
             PhysicalType::INT96 => {
@@ -266,8 +396,16 @@ impl ColumnReader {
                         } => {
                             if !use_decimal_128 && precision <= 
DECIMAL_MAX_INT_DIGITS {
                                 typed_reader!(FLBADecimal32ColumnReader, Int32)
-                            } else if !use_decimal_128 && precision <= 
DECIMAL_MAX_LONG_DIGITS {
-                                typed_reader!(FLBADecimal64ColumnReader, Int64)
+                            } else if !use_decimal_128
+                                && promotion_info.precision <= 
DECIMAL_MAX_LONG_DIGITS
+                            {
+                                typed_reader!(
+                                    FLBADecimal64ColumnReader,
+                                    ArrowDataType::Decimal128(
+                                        promotion_info.precision as u8,
+                                        promotion_info.scale as i8
+                                    )
+                                )
                             } else {
                                 typed_reader!(
                                     FLBADecimalColumnReader,
@@ -306,13 +444,18 @@ macro_rules! make_func {
             Self::Int8ColumnReader(ref typed) => typed.$func($($args),*),
             Self::UInt8ColumnReader(ref typed) => typed.$func($($args),*),
             Self::Int16ColumnReader(ref typed) => typed.$func($($args),*),
+            Self::Int16ToDoubleColumnReader(ref typed) => 
typed.$func($($args), *),
             Self::UInt16ColumnReader(ref typed) => typed.$func($($args),*),
             Self::Int32ColumnReader(ref typed) => typed.$func($($args),*),
             Self::Int32To64ColumnReader(ref typed) => typed.$func($($args), *),
+            Self::Int32ToDecimal64ColumnReader(ref typed) => 
typed.$func($($args), *),
+            Self::Int32ToDoubleColumnReader(ref typed) => 
typed.$func($($args), *),
             Self::UInt32ColumnReader(ref typed) => typed.$func($($args),*),
             Self::Int32DateColumnReader(ref typed) => typed.$func($($args),*),
             Self::Int32DecimalColumnReader(ref typed) => 
typed.$func($($args),*),
+            Self::Int32TimestampMicrosColumnReader(ref typed) => 
typed.$func($($args),*),
             Self::Int64ColumnReader(ref typed) => typed.$func($($args),*),
+            Self::Int64ToDecimal64ColumnReader(ref typed) => 
typed.$func($($args), *),
             Self::UInt64DecimalColumnReader(ref typed) => 
typed.$func($($args),*),
             Self::Int64DecimalColumnReader(ref typed) => 
typed.$func($($args),*),
             Self::Int64TimestampMillisColumnReader(ref typed) => 
typed.$func($($args),*),
@@ -339,13 +482,18 @@ macro_rules! make_func_mut {
             Self::Int8ColumnReader(ref mut typed) => typed.$func($($args),*),
             Self::UInt8ColumnReader(ref mut typed) => typed.$func($($args),*),
             Self::Int16ColumnReader(ref mut typed) => typed.$func($($args),*),
+            Self::Int16ToDoubleColumnReader(ref mut typed) => 
typed.$func($($args), *),
             Self::UInt16ColumnReader(ref mut typed) => typed.$func($($args),*),
             Self::Int32ColumnReader(ref mut typed) => typed.$func($($args),*),
             Self::Int32To64ColumnReader(ref mut typed) => 
typed.$func($($args), *),
+            Self::Int32ToDecimal64ColumnReader(ref mut typed) => 
typed.$func($($args), *),
+            Self::Int32ToDoubleColumnReader(ref mut typed) => 
typed.$func($($args), *),
             Self::UInt32ColumnReader(ref mut typed) => typed.$func($($args),*),
             Self::Int32DateColumnReader(ref mut typed) => 
typed.$func($($args),*),
             Self::Int32DecimalColumnReader(ref mut typed) => 
typed.$func($($args),*),
+            Self::Int32TimestampMicrosColumnReader(ref mut typed) => 
typed.$func($($args),*),
             Self::Int64ColumnReader(ref mut typed) => typed.$func($($args),*),
+            Self::Int64ToDecimal64ColumnReader(ref mut typed) => 
typed.$func($($args), *),
             Self::UInt64DecimalColumnReader(ref mut typed) => 
typed.$func($($args),*),
             Self::Int64DecimalColumnReader(ref mut typed) => 
typed.$func($($args),*),
             Self::Int64TimestampMillisColumnReader(ref mut typed) => 
typed.$func($($args),*),
diff --git a/native/core/src/parquet/read/levels.rs 
b/native/core/src/parquet/read/levels.rs
index d43c2868..303db54c 100644
--- a/native/core/src/parquet/read/levels.rs
+++ b/native/core/src/parquet/read/levels.rs
@@ -121,8 +121,8 @@ impl LevelDecoder {
             match self.mode {
                 Mode::RLE => {
                     if self.current_value as i16 == max_def_level {
-                        value_decoder.read_batch(vector, n);
                         bit::set_bits(vector.validity_buffer.as_slice_mut(), 
vector.num_values, n);
+                        value_decoder.read_batch(vector, n);
                         vector.num_values += n;
                     } else {
                         vector.put_nulls(n);
@@ -132,8 +132,8 @@ impl LevelDecoder {
                     for i in 0..n {
                         if self.current_buffer[self.current_buffer_idx + i] == 
max_def_level as i32
                         {
-                            value_decoder.read(vector);
                             
bit::set_bit(vector.validity_buffer.as_slice_mut(), vector.num_values);
+                            value_decoder.read(vector);
                             vector.num_values += 1;
                         } else {
                             vector.put_null();
diff --git a/native/core/src/parquet/read/values.rs 
b/native/core/src/parquet/read/values.rs
index ebed5f95..8eae330a 100644
--- a/native/core/src/parquet/read/values.rs
+++ b/native/core/src/parquet/read/values.rs
@@ -23,6 +23,8 @@ use log::debug;
 use parquet::{basic::Encoding, schema::types::ColumnDescPtr};
 
 use super::{PlainDecoderInner, PlainDecoding, PlainDictDecoding, ReadOptions};
+use crate::write_null;
+use crate::write_val_or_null;
 use crate::{
     common::bit::{self, BitReader},
     parquet::{data_type::*, read::DECIMAL_BYTE_WIDTH, ParquetMutableVector},
@@ -180,63 +182,38 @@ macro_rules! make_plain_dict_impl {
 }
 
 make_plain_dict_impl! { Int8Type, UInt8Type, Int16Type, UInt16Type, Int32Type, 
UInt32Type }
-make_plain_dict_impl! { Int32DateType, Int64Type, FloatType, FLBAType }
+make_plain_dict_impl! { Int32DateType, Int64Type, FloatType, 
Int64ToDecimal64Type, FLBAType }
 make_plain_dict_impl! { DoubleType, Int64TimestampMillisType, 
Int64TimestampMicrosType }
 
-impl PlainDictDecoding for Int32To64Type {
-    fn decode_dict_one(
-        idx: usize,
-        val_idx: usize,
-        src: &ParquetMutableVector,
-        dst: &mut ParquetMutableVector,
-        _: usize,
-    ) {
-        let src_ptr = src.value_buffer.as_ptr() as *const i32;
-        let dst_ptr = dst.value_buffer.as_mut_ptr() as *mut i64;
-        unsafe {
-            dst_ptr
-                .add(idx)
-                .write_unaligned(src_ptr.add(val_idx).read_unaligned() as i64);
-        }
-    }
-}
-
-impl PlainDecoding for FloatToDoubleType {
-    fn decode(src: &mut PlainDecoderInner, dst: &mut ParquetMutableVector, 
num: usize) {
-        let src_ptr = src.data.as_ptr() as *const f32;
-        let dst_ptr = dst.value_buffer.as_mut_ptr() as *mut f64;
-        unsafe {
-            for i in 0..num {
-                dst_ptr
-                    .add(dst.num_values + i)
-                    .write_unaligned(src_ptr.add(src.offset + 
i).read_unaligned() as f64);
+macro_rules! make_int_variant_dict_impl {
+    ($ty:ty, $src_ty:ty, $dst_ty:ty) => {
+        impl PlainDictDecoding for $ty {
+            fn decode_dict_one(
+                idx: usize,
+                val_idx: usize,
+                src: &ParquetMutableVector,
+                dst: &mut ParquetMutableVector,
+                _: usize,
+            ) {
+                let src_ptr = src.value_buffer.as_ptr() as *const $src_ty;
+                let dst_ptr = dst.value_buffer.as_mut_ptr() as *mut $dst_ty;
+                unsafe {
+                    // SAFETY the caller must ensure `idx`th pointer is in 
bounds
+                    dst_ptr
+                        .add(idx)
+                        .write_unaligned(src_ptr.add(val_idx).read_unaligned() 
as $dst_ty);
+                }
             }
         }
-        src.offset += 4 * num;
-    }
-
-    fn skip(src: &mut PlainDecoderInner, num: usize) {
-        src.offset += 4 * num;
-    }
+    };
 }
 
-impl PlainDictDecoding for FloatToDoubleType {
-    fn decode_dict_one(
-        idx: usize,
-        val_idx: usize,
-        src: &ParquetMutableVector,
-        dst: &mut ParquetMutableVector,
-        _: usize,
-    ) {
-        let src_ptr = src.value_buffer.as_ptr() as *const f32;
-        let dst_ptr = dst.value_buffer.as_mut_ptr() as *mut f64;
-        unsafe {
-            dst_ptr
-                .add(idx)
-                .write_unaligned(src_ptr.add(val_idx).read_unaligned() as f64);
-        }
-    }
-}
+make_int_variant_dict_impl!(Int16ToDoubleType, i16, f64);
+make_int_variant_dict_impl!(Int32To64Type, i32, i64);
+make_int_variant_dict_impl!(Int32ToDecimal64Type, i32, i64);
+make_int_variant_dict_impl!(Int32ToDoubleType, i32, f64);
+make_int_variant_dict_impl!(Int32TimestampMicrosType, i32, i64);
+make_int_variant_dict_impl!(FloatToDoubleType, f32, f64);
 
 impl PlainDecoding for Int32DateType {
     fn decode(src: &mut PlainDecoderInner, dst: &mut ParquetMutableVector, 
num: usize) {
@@ -259,7 +236,7 @@ impl PlainDecoding for Int32DateType {
                     if unlikely(v.read_unaligned() < 
JULIAN_GREGORIAN_SWITCH_OFF_DAY) {
                         panic!(
                         "Encountered date value {}, which is before 1582-10-15 
(counting backwards \
-                         from Unix eopch date 1970-01-01), and could be 
ambigous depending on \
+                         from Unix epoch date 1970-01-01), and could be 
ambigous depending on \
                          whether a legacy Julian/Gregorian hybrid calendar is 
used, or a Proleptic \
                          Gregorian calendar is used.",
                         *v
@@ -285,6 +262,57 @@ impl PlainDecoding for Int32DateType {
     }
 }
 
+impl PlainDecoding for Int32TimestampMicrosType {
+    #[inline]
+    fn decode(src: &mut PlainDecoderInner, dst: &mut ParquetMutableVector, 
num: usize) {
+        let src_data = &src.data;
+        let byte_width = src.desc.type_length() as usize;
+        let num_bytes = byte_width * num;
+
+        {
+            let mut offset = src.offset;
+            for _ in 0..num {
+                let v = src_data[offset..offset + byte_width].as_ptr() as 
*const i32;
+
+                // TODO: optimize this further as checking value one by one is 
not very efficient
+                unsafe {
+                    if unlikely(v.read_unaligned() < 
JULIAN_GREGORIAN_SWITCH_OFF_DAY) {
+                        panic!(
+                            "Encountered timestamp value {}, which is before 
1582-10-15 (counting \
+                        backwards from Unix epoch date 1970-01-01), and could 
be ambigous \
+                        depending on whether a legacy Julian/Gregorian hybrid 
calendar is used, \
+                        or a Proleptic Gregorian calendar is used.",
+                            *v
+                        );
+                    }
+                }
+
+                offset += byte_width;
+            }
+        }
+
+        let mut offset = src.offset;
+        let dst_byte_width = byte_width * 2;
+        let mut dst_offset = dst_byte_width * dst.num_values;
+        for _ in 0..num {
+            let v = src_data[offset..offset + byte_width].as_ptr() as *const 
i32;
+            let v = unsafe { v.read_unaligned() };
+            let v = (v as i64).wrapping_mul(MICROS_PER_DAY);
+            bit::memcpy_value(&v, dst_byte_width, &mut 
dst.value_buffer[dst_offset..]);
+            offset += byte_width;
+            dst_offset += dst_byte_width;
+        }
+
+        src.offset += num_bytes;
+    }
+
+    #[inline]
+    fn skip(src: &mut PlainDecoderInner, num: usize) {
+        let num_bytes = src.desc.type_length() as usize * num;
+        src.offset += num_bytes;
+    }
+}
+
 impl PlainDecoding for Int64TimestampMillisType {
     #[inline]
     fn decode(src: &mut PlainDecoderInner, dst: &mut ParquetMutableVector, 
num: usize) {
@@ -305,7 +333,7 @@ impl PlainDecoding for Int64TimestampMillisType {
                     if unlikely(v < JULIAN_GREGORIAN_SWITCH_OFF_TS) {
                         panic!(
                             "Encountered timestamp value {}, which is before 
1582-10-15 (counting \
-                         backwards from Unix eopch date 1970-01-01), and could 
be ambigous \
+                         backwards from Unix epoch date 1970-01-01), and could 
be ambigous \
                          depending on whether a legacy Julian/Gregorian hybrid 
calendar is used, \
                          or a Proleptic Gregorian calendar is used.",
                             v
@@ -360,7 +388,7 @@ impl PlainDecoding for Int64TimestampMicrosType {
                     if unlikely(v.read_unaligned() < 
JULIAN_GREGORIAN_SWITCH_OFF_TS) {
                         panic!(
                             "Encountered timestamp value {}, which is before 
1582-10-15 (counting \
-                         backwards from Unix eopch date 1970-01-01), and could 
be ambigous \
+                         backwards from Unix epoch date 1970-01-01), and could 
be ambigous \
                          depending on whether a legacy Julian/Gregorian hybrid 
calendar is used, \
                          or a Proleptic Gregorian calendar is used.",
                             *v
@@ -439,7 +467,10 @@ macro_rules! make_int_variant_impl {
 
 make_int_variant_impl!(Int8Type, copy_i32_to_i8, 1);
 make_int_variant_impl!(Int16Type, copy_i32_to_i16, 2);
-make_int_variant_impl!(Int32To64Type, copy_i32_to_i64, 4);
+make_int_variant_impl!(Int16ToDoubleType, copy_i32_to_f64, 8); // Parquet uses 
Int16 using 4 bytes
+make_int_variant_impl!(Int32To64Type, copy_i32_to_i64, 8);
+make_int_variant_impl!(Int32ToDoubleType, copy_i32_to_f64, 8);
+make_int_variant_impl!(FloatToDoubleType, copy_f32_to_f64, 8);
 
 // unsigned type require double the width and zeroes are written for the 
second half
 // perhaps because they are implemented as the next size up signed type?
@@ -447,6 +478,81 @@ make_int_variant_impl!(UInt8Type, copy_i32_to_u8, 2);
 make_int_variant_impl!(UInt16Type, copy_i32_to_u16, 4);
 make_int_variant_impl!(UInt32Type, copy_i32_to_u32, 8);
 
+macro_rules! make_int_decimal_variant_impl {
+    ($ty:ty, $copy_fn:ident, $type_width:expr, $dst_type:ty) => {
+        impl PlainDecoding for $ty {
+            fn decode(src: &mut PlainDecoderInner, dst: &mut 
ParquetMutableVector, num: usize) {
+                let dst_slice = dst.value_buffer.as_slice_mut();
+                let dst_offset = dst.num_values * 8;
+                $copy_fn(&src.data[src.offset..], &mut 
dst_slice[dst_offset..], num);
+
+                let src_precision = src.desc.type_precision() as u32;
+                let src_scale = ::std::cmp::max(src.desc.type_scale(), 0) as 
u32;
+                let (dst_precision, dst_scale) = match dst.arrow_type {
+                    ArrowDataType::Decimal128(p, s) if s >= 0 => (p as u32, s 
as u32),
+                    _ => unreachable!(),
+                };
+                let upper = (10 as $dst_type).pow(dst_precision);
+                let v = dst_slice[dst_offset..].as_mut_ptr() as *mut $dst_type;
+                if dst_scale > src_scale {
+                    let mul = (10 as $dst_type).pow(dst_scale - src_scale);
+                    for i in 0..num {
+                        unsafe {
+                            // SAFETY the caller must ensure `i`th pointer is 
in bounds
+                            let v = v.add(i);
+                            write_val_or_null!(v, v.read_unaligned() * mul, 
upper, dst, i);
+                        }
+                    }
+                } else if dst_scale < src_scale {
+                    let div = (10 as $dst_type).pow(src_scale - dst_scale);
+                    for i in 0..num {
+                        unsafe {
+                            // SAFETY the caller must ensure `i`th pointer is 
in bounds
+                            let v = v.add(i);
+                            write_val_or_null!(v, v.read_unaligned() / div, 
upper, dst, i);
+                        }
+                    }
+                } else if src_precision > dst_precision {
+                    for i in 0..num {
+                        unsafe {
+                            // SAFETY the caller must ensure `i`th pointer is 
in bounds
+                            let v = v.add(i);
+                            write_null!(v.read_unaligned(), upper, dst, i);
+                        }
+                    }
+                }
+
+                src.offset += $type_width * num;
+            }
+
+            fn skip(src: &mut PlainDecoderInner, num: usize) {
+                src.offset += $type_width * num;
+            }
+        }
+    };
+}
+make_int_decimal_variant_impl!(Int32ToDecimal64Type, copy_i32_to_i64, 4, i64);
+make_int_decimal_variant_impl!(Int64ToDecimal64Type, copy_i64_to_i64, 8, i64);
+
+#[macro_export]
+macro_rules! write_val_or_null {
+    ($v: expr, $adjusted: expr, $upper: expr, $dst: expr, $i: expr) => {
+        let adjusted = $adjusted;
+        $v.write_unaligned(adjusted);
+        write_null!(adjusted, $upper, $dst, $i);
+    };
+}
+
+#[macro_export]
+macro_rules! write_null {
+    ($val: expr, $upper: expr, $dst: expr, $i: expr) => {
+        if $upper <= $val {
+            bit::unset_bit($dst.validity_buffer.as_slice_mut(), 
$dst.num_values + $i);
+            $dst.num_nulls += 1;
+        }
+    };
+}
+
 macro_rules! generate_cast_to_unsigned {
     ($name: ident, $src_type:ty, $dst_type:ty, $zero_value:expr) => {
         pub fn $name(src: &[u8], dst: &mut [u8], num: usize) {
@@ -506,6 +612,21 @@ macro_rules! generate_cast_to_signed {
 generate_cast_to_signed!(copy_i32_to_i8, i32, i8);
 generate_cast_to_signed!(copy_i32_to_i16, i32, i16);
 generate_cast_to_signed!(copy_i32_to_i64, i32, i64);
+generate_cast_to_signed!(copy_i32_to_f64, i32, f64);
+generate_cast_to_signed!(copy_f32_to_f64, f32, f64);
+
+fn copy_i64_to_i64(src: &[u8], dst: &mut [u8], num: usize) {
+    debug_assert!(
+        src.len() >= num * std::mem::size_of::<i64>(),
+        "Source slice is too small"
+    );
+    debug_assert!(
+        dst.len() >= num * std::mem::size_of::<i64>(),
+        "Destination slice is too small"
+    );
+
+    bit::memcpy_value(src, std::mem::size_of::<i64>() * num, dst);
+}
 
 // Shared implementation for variants of Binary type
 macro_rules! make_plain_binary_impl {
@@ -652,13 +773,16 @@ macro_rules! make_plain_decimal_impl {
 
                     debug_assert!(byte_width <= DECIMAL_BYTE_WIDTH);
 
-                    let src_scale = src.desc.type_scale() as u32;
-                    let dst_scale = match dst.arrow_type {
-                        ArrowDataType::Decimal128(_percision, scale) => scale 
as u32,
-                        _ => unreachable!()
+                    let src_precision = src.desc.type_precision() as u32;
+                    let src_scale = ::std::cmp::max(src.desc.type_scale(), 0) 
as u32;
+                    let (dst_precision, dst_scale) = match dst.arrow_type {
+                        ArrowDataType::Decimal128(p, s) if s >= 0 => (p as 
u32, s as u32),
+                        _ => unreachable!(),
                     };
+                    let upper = 10_i128.pow(dst_precision);
+                    let mul_div = 10_i128.pow(dst_scale.abs_diff(src_scale));
 
-                    for _ in 0..num {
+                    for i in 0..num {
                         let s = &mut dst_data[dst_offset..];
 
                         bit::memcpy(
@@ -682,12 +806,20 @@ macro_rules! make_plain_decimal_impl {
                         }
 
                         if dst_scale > src_scale {
-                            let exp = dst_scale - src_scale;
-                            let mul = 10_i128.pow(exp);
                             let v = s.as_mut_ptr() as *mut i128;
                             unsafe {
-                                 v.write_unaligned(v.read_unaligned() * mul);
+                                // SAFETY the caller must ensure `i`th pointer 
is in bounds
+                                write_val_or_null!(v, v.read_unaligned() * 
mul_div, upper, dst, i);
+                            }
+                        } else if dst_scale < src_scale {
+                            let v = s.as_mut_ptr() as *mut i128;
+                            unsafe {
+                                // SAFETY the caller must ensure `i`th pointer 
is in bounds
+                                write_val_or_null!(v, v.read_unaligned() / 
mul_div, upper, dst, i);
                             }
+                        } else  if src_precision > dst_precision {
+                            let v = s.as_mut_ptr() as *mut i128;
+                            write_null!(unsafe { v.read_unaligned() }, upper, 
dst, i);
                         }
 
                         src_offset += byte_width;
@@ -728,13 +860,22 @@ macro_rules! make_plain_decimal_int_impl {
             impl PlainDecoding for $ty {
                 fn decode(src: &mut PlainDecoderInner, dst: &mut 
ParquetMutableVector, num: usize) {
                     let byte_width = src.desc.type_length() as usize;
-                    let num_bits = 8 * byte_width;
+                    let num_bits = 64.min(8 * byte_width);
 
                     let src_data = &src.data[src.offset..];
                     let dst_data = &mut dst.value_buffer[dst.num_values * 
$num_bytes..];
 
                     let mut src_offset = 0;
 
+                    let src_precision = src.desc.type_precision() as u32;
+                    let src_scale = ::std::cmp::max(src.desc.type_scale(), 0) 
as u32;
+                    let (dst_precision, dst_scale) = match dst.arrow_type {
+                        ArrowDataType::Decimal128(p, s) if s >= 0 => (p as 
u32, s as u32),
+                        _ => (src_precision, src_scale),
+                    };
+                    let upper = 10_i64.pow(dst_precision);
+                    let mul_div = 10_i64.pow(dst_scale.abs_diff(src_scale));
+
                     for i in 0..num {
                         let mut unscaled: i64 = 0;
                         for _ in 0..byte_width {
@@ -742,8 +883,15 @@ macro_rules! make_plain_decimal_int_impl {
                             src_offset += 1;
                         }
                         unscaled = (unscaled << (64 - num_bits)) >> (64 - 
num_bits);
-                        bit::memcpy_value(&unscaled, $num_bytes, &mut 
dst_data[i *
-                        $num_bytes..]);
+                        if dst_scale > src_scale {
+                            unscaled *= mul_div;
+                        } else if dst_scale < src_scale {
+                            unscaled /= mul_div;
+                        }
+                        bit::memcpy_value(&unscaled, $num_bytes, &mut 
dst_data[i * $num_bytes..]);
+                        if src_precision > dst_precision {
+                            write_null!(unscaled, upper, dst, i);
+                        }
                     }
 
                     src.offset += num * byte_width;
@@ -801,7 +949,7 @@ impl PlainDecoding for Int96TimestampMicrosType {
                 if unlikely(micros < JULIAN_GREGORIAN_SWITCH_OFF_TS) {
                     panic!(
                         "Encountered timestamp value {}, which is before 
1582-10-15 (counting \
-                         backwards from Unix eopch date 1970-01-01), and could 
be ambigous \
+                         backwards from Unix epoch date 1970-01-01), and could 
be ambigous \
                          depending on whether a legacy Julian/Gregorian hybrid 
calendar is used, \
                          or a Proleptic Gregorian calendar is used.",
                         micros
diff --git a/native/core/src/parquet/util/jni.rs 
b/native/core/src/parquet/util/jni.rs
index cde9fff0..bcd82c6c 100644
--- a/native/core/src/parquet/util/jni.rs
+++ b/native/core/src/parquet/util/jni.rs
@@ -97,23 +97,31 @@ pub struct TypePromotionInfo {
     pub(crate) physical_type: PhysicalType,
     pub(crate) precision: i32,
     pub(crate) scale: i32,
+    pub(crate) bit_width: i32,
 }
 
 impl TypePromotionInfo {
-    pub fn new_from_jni(physical_type_id: jint, precision: jint, scale: jint) 
-> Self {
+    pub fn new_from_jni(
+        physical_type_id: jint,
+        precision: jint,
+        scale: jint,
+        bit_width: jint,
+    ) -> Self {
         let physical_type = convert_physical_type(physical_type_id);
         Self {
             physical_type,
             precision,
             scale,
+            bit_width,
         }
     }
 
-    pub fn new(physical_type: PhysicalType, precision: i32, scale: i32) -> 
Self {
+    pub fn new(physical_type: PhysicalType, precision: i32, scale: i32, 
bit_width: i32) -> Self {
         Self {
             physical_type,
             precision,
             scale,
+            bit_width,
         }
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to