You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by pr...@apache.org on 2020/03/06 11:19:36 UTC

[arrow] branch master updated: ARROW-7993: [Java] Support decimal type in ComplexCopier

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

praveenbingo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/master by this push:
     new 88e3267  ARROW-7993: [Java] Support decimal type in ComplexCopier
88e3267 is described below

commit 88e3267ad09ca62643b7fe5ffd98eb03b29728fc
Author: Projjal Chanda <ia...@pchanda.com>
AuthorDate: Fri Mar 6 16:49:09 2020 +0530

    ARROW-7993: [Java] Support decimal type in ComplexCopier
    
    Closes #6523 from projjal/decimalcomplexcopier and squashes the following commits:
    
    105e94eb0 <Projjal Chanda> added missing methods
    0f0bb2646 <Projjal Chanda> added test
    b523e957e <Projjal Chanda> added fix
    45a2c0720 <Projjal Chanda> Added more tests
    574f13c49 <Projjal Chanda> added (some) review comments
    dc849c139 <Projjal Chanda> Support DecimalType in ComplexCopier
    
    Authored-by: Projjal Chanda <ia...@pchanda.com>
    Signed-off-by: Praveen <pr...@dremio.com>
---
 .../codegen/templates/AbstractFieldWriter.java     |   8 +
 .../templates/AbstractPromotableFieldWriter.java   |  20 ++-
 .../src/main/codegen/templates/ComplexCopier.java  |  17 ++-
 .../src/main/codegen/templates/ComplexWriters.java |  55 ++++++-
 .../main/codegen/templates/DenseUnionReader.java   |   4 +-
 .../main/codegen/templates/DenseUnionVector.java   |  16 +-
 .../main/codegen/templates/DenseUnionWriter.java   |  18 ++-
 .../src/main/codegen/templates/StructWriters.java  |   2 +-
 .../templates/UnionFixedSizeListWriter.java        |   4 +-
 .../main/codegen/templates/UnionListWriter.java    |   5 +
 .../src/main/codegen/templates/UnionReader.java    |   4 +-
 .../src/main/codegen/templates/UnionVector.java    |  49 +++++--
 .../src/main/codegen/templates/UnionWriter.java    |  49 +++++--
 .../org/apache/arrow/vector/DecimalVector.java     |   7 +
 .../vector/complex/impl/PromotableWriter.java      |  48 +++---
 .../apache/arrow/vector/util/DecimalUtility.java   |  18 +++
 .../vector/complex/impl/TestComplexCopier.java     | 162 ++++++++++++++++++++-
 17 files changed, 402 insertions(+), 84 deletions(-)

diff --git a/java/vector/src/main/codegen/templates/AbstractFieldWriter.java b/java/vector/src/main/codegen/templates/AbstractFieldWriter.java
index c4ac32c..4f6d5ea 100644
--- a/java/vector/src/main/codegen/templates/AbstractFieldWriter.java
+++ b/java/vector/src/main/codegen/templates/AbstractFieldWriter.java
@@ -81,9 +81,17 @@ abstract class AbstractFieldWriter extends AbstractBaseWriter implements FieldWr
     fail("${name}");
   }
 
+  public void write${minor.class}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, </#if></#list><#if minor.class == "Decimal">, ArrowType arrowType</#if>) {
+    fail("${name}");
+  }
+
   public void writeBigEndianBytesToDecimal(byte[] value) {
     fail("${name}");
   }
+
+  public void writeBigEndianBytesToDecimal(byte[] value, ArrowType arrowType) {
+    fail("${name}");
+  }
   </#if>
 
   </#list></#list>
diff --git a/java/vector/src/main/codegen/templates/AbstractPromotableFieldWriter.java b/java/vector/src/main/codegen/templates/AbstractPromotableFieldWriter.java
index 77ac5ba..92b9cd2 100644
--- a/java/vector/src/main/codegen/templates/AbstractPromotableFieldWriter.java
+++ b/java/vector/src/main/codegen/templates/AbstractPromotableFieldWriter.java
@@ -75,6 +75,7 @@ abstract class AbstractPromotableFieldWriter extends AbstractFieldWriter {
 
   <#list vv.types as type><#list type.minor as minor><#assign name = minor.class?cap_first />
     <#assign fields = minor.fields!type.fields />
+  <#if minor.class != "Decimal">
   @Override
   public void write(${name}Holder holder) {
     getWriter(MinorType.${name?upper_case}).write(holder);
@@ -84,7 +85,24 @@ abstract class AbstractPromotableFieldWriter extends AbstractFieldWriter {
     getWriter(MinorType.${name?upper_case}).write${minor.class}(<#list fields as field>${field.name}<#if field_has_next>, </#if></#list>);
   }
 
-  <#if minor.class == "Decimal">
+  <#else>
+  @Override
+  public void write(DecimalHolder holder) {
+    getWriter(MinorType.DECIMAL).write(holder);
+  }
+
+  public void writeDecimal(int start, ArrowBuf buffer, ArrowType arrowType) {
+    getWriter(MinorType.DECIMAL).writeDecimal(start, buffer, arrowType);
+  }
+
+  public void writeDecimal(int start, ArrowBuf buffer) {
+    getWriter(MinorType.DECIMAL).writeDecimal(start, buffer);
+  }
+
+  public void writeBigEndianBytesToDecimal(byte[] value, ArrowType arrowType) {
+    getWriter(MinorType.DECIMAL).writeBigEndianBytesToDecimal(value, arrowType);
+  }
+
   public void writeBigEndianBytesToDecimal(byte[] value) {
     getWriter(MinorType.DECIMAL).writeBigEndianBytesToDecimal(value);
   }
diff --git a/java/vector/src/main/codegen/templates/ComplexCopier.java b/java/vector/src/main/codegen/templates/ComplexCopier.java
index ebf6c0f..270be08 100644
--- a/java/vector/src/main/codegen/templates/ComplexCopier.java
+++ b/java/vector/src/main/codegen/templates/ComplexCopier.java
@@ -96,14 +96,14 @@ public class ComplexCopier {
   <#assign fields = minor.fields!type.fields />
   <#assign uncappedName = name?uncap_first/>
 
-  <#if !minor.typeParams?? >
+  <#if !minor.typeParams?? || minor.class?starts_with("Decimal") >
 
       case ${name?upper_case}:
         if (reader.isSet()) {
           Nullable${name}Holder ${uncappedName}Holder = new Nullable${name}Holder();
           reader.read(${uncappedName}Holder);
           if (${uncappedName}Holder.isSet == 1) {
-            writer.write${name}(<#list fields as field>${uncappedName}Holder.${field.name}<#if field_has_next>, </#if></#list>);
+            writer.write${name}(<#list fields as field>${uncappedName}Holder.${field.name}<#if field_has_next>, </#if></#list><#if minor.class == "Decimal">, new ArrowType.Decimal(decimalHolder.precision, decimalHolder.scale)</#if>);
           }
         }
         break;
@@ -118,10 +118,19 @@ public class ComplexCopier {
     <#list vv.types as type><#list type.minor as minor><#assign name = minor.class?cap_first />
     <#assign fields = minor.fields!type.fields />
     <#assign uncappedName = name?uncap_first/>
-    <#if !minor.typeParams?? >
+    <#if !minor.typeParams??>
     case ${name?upper_case}:
       return (FieldWriter) writer.<#if name == "Int">integer<#else>${uncappedName}</#if>(name);
     </#if>
+    <#if minor.class == "Decimal">
+    case ${name?upper_case}:
+      if (reader.getField().getType() instanceof ArrowType.Decimal) {
+        ArrowType.Decimal type = (ArrowType.Decimal) reader.getField().getType();
+        return (FieldWriter) writer.${uncappedName}(name, type.getScale(), type.getPrecision());
+      } else {
+        return (FieldWriter) writer.${uncappedName}(name);
+      }
+    </#if>
     </#list></#list>
     case STRUCT:
       return (FieldWriter) writer.struct(name);
@@ -139,7 +148,7 @@ public class ComplexCopier {
     <#list vv.types as type><#list type.minor as minor><#assign name = minor.class?cap_first />
     <#assign fields = minor.fields!type.fields />
     <#assign uncappedName = name?uncap_first/>
-    <#if !minor.typeParams?? >
+    <#if !minor.typeParams?? || minor.class?starts_with("Decimal") >
     case ${name?upper_case}:
     return (FieldWriter) writer.<#if name == "Int">integer<#else>${uncappedName}</#if>();
     </#if>
diff --git a/java/vector/src/main/codegen/templates/ComplexWriters.java b/java/vector/src/main/codegen/templates/ComplexWriters.java
index 9d943e4..ab99ac3 100644
--- a/java/vector/src/main/codegen/templates/ComplexWriters.java
+++ b/java/vector/src/main/codegen/templates/ComplexWriters.java
@@ -99,6 +99,7 @@ public class ${eName}WriterImpl extends AbstractFieldWriter {
 
   <#else>
 
+  <#if minor.class != "Decimal">
   public void write(${minor.class}Holder h) {
     vector.setSafe(idx(), h);
     vector.setValueCount(idx()+1);
@@ -113,9 +114,9 @@ public class ${eName}WriterImpl extends AbstractFieldWriter {
     vector.setSafe(idx(), 1<#list fields as field><#if field.include!true >, ${field.name}</#if></#list>);
     vector.setValueCount(idx()+1);
   }
+  </#if>
 
-  <#if minor.class == "Decimal" ||
-       minor.class == "VarChar">
+  <#if minor.class == "VarChar">
   public void write${minor.class}(${friendlyType} value) {
     vector.setSafe(idx(), value);
     vector.setValueCount(idx()+1);
@@ -123,9 +124,49 @@ public class ${eName}WriterImpl extends AbstractFieldWriter {
   </#if>
 
   <#if minor.class == "Decimal">
-  public void writeBigEndianBytesToDecimal(byte[] value) {
+
+  public void write(DecimalHolder h){
+    DecimalUtility.checkPrecisionAndScale(h.precision, h.scale, vector.getPrecision(), vector.getScale());
+    vector.setSafe(idx(), h);
+    vector.setValueCount(idx() + 1);
+  }
+
+  public void write(NullableDecimalHolder h){
+    if (h.isSet == 1) {
+      DecimalUtility.checkPrecisionAndScale(h.precision, h.scale, vector.getPrecision(), vector.getScale());
+    }
+    vector.setSafe(idx(), h);
+    vector.setValueCount(idx() + 1);
+  }
+
+  public void writeDecimal(int start, ArrowBuf buffer){
+    vector.setSafe(idx(), 1, start, buffer);
+    vector.setValueCount(idx() + 1);
+  }
+
+  public void writeDecimal(int start, ArrowBuf buffer, ArrowType arrowType){
+    DecimalUtility.checkPrecisionAndScale(((ArrowType.Decimal) arrowType).getPrecision(),
+      ((ArrowType.Decimal) arrowType).getScale(), vector.getPrecision(), vector.getScale());
+    vector.setSafe(idx(), 1, start, buffer);
+    vector.setValueCount(idx() + 1);
+  }
+
+  public void writeDecimal(BigDecimal value){
+    // vector.setSafe already does precision and scale checking
+    vector.setSafe(idx(), value);
+    vector.setValueCount(idx() + 1);
+  }
+
+  public void writeBigEndianBytesToDecimal(byte[] value, ArrowType arrowType){
+    DecimalUtility.checkPrecisionAndScale(((ArrowType.Decimal) arrowType).getPrecision(),
+        ((ArrowType.Decimal) arrowType).getScale(), vector.getPrecision(), vector.getScale());
     vector.setBigEndianSafe(idx(), value);
-    vector.setValueCount(idx()+1);
+    vector.setValueCount(idx() + 1);
+  }
+
+  public void writeBigEndianBytesToDecimal(byte[] value){
+    vector.setBigEndianSafe(idx(), value);
+    vector.setValueCount(idx() + 1);
   }
   </#if>
 
@@ -149,11 +190,17 @@ package org.apache.arrow.vector.complex.writer;
 public interface ${eName}Writer extends BaseWriter {
   public void write(${minor.class}Holder h);
 
+  <#if minor.class == "Decimal">@Deprecated</#if>
   public void write${minor.class}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, </#if></#list>);
 <#if minor.class == "Decimal">
 
+  public void write${minor.class}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, </#if></#list>, ArrowType arrowType);
+
   public void write${minor.class}(${friendlyType} value);
 
+  public void writeBigEndianBytesToDecimal(byte[] value, ArrowType arrowType);
+
+  @Deprecated
   public void writeBigEndianBytesToDecimal(byte[] value);
 </#if>
 }
diff --git a/java/vector/src/main/codegen/templates/DenseUnionReader.java b/java/vector/src/main/codegen/templates/DenseUnionReader.java
index 6e3d19b..51bd7d1 100644
--- a/java/vector/src/main/codegen/templates/DenseUnionReader.java
+++ b/java/vector/src/main/codegen/templates/DenseUnionReader.java
@@ -92,7 +92,7 @@ public class DenseUnionReader extends AbstractFieldReader {
       <#list type.minor as minor>
         <#assign name = minor.class?cap_first />
         <#assign uncappedName = name?uncap_first/>
-        <#if !minor.typeParams?? >
+        <#if !minor.typeParams?? || minor.class == "Decimal">
       case ${name?upper_case}:
       reader = (FieldReader) get${name}(typeId);
       break;
@@ -165,7 +165,7 @@ public class DenseUnionReader extends AbstractFieldReader {
       <#assign friendlyType = (minor.friendlyType!minor.boxedType!type.boxedType) />
       <#assign safeType=friendlyType />
       <#if safeType=="byte[]"><#assign safeType="ByteArray" /></#if>
-      <#if !minor.typeParams?? >
+      <#if !minor.typeParams?? || minor.class == "Decimal">
 
   private ${name}ReaderImpl get${name}(byte typeId) {
     ${name}ReaderImpl reader = (${name}ReaderImpl) readers[typeId];
diff --git a/java/vector/src/main/codegen/templates/DenseUnionVector.java b/java/vector/src/main/codegen/templates/DenseUnionVector.java
index 31ffd78..9efab95 100644
--- a/java/vector/src/main/codegen/templates/DenseUnionVector.java
+++ b/java/vector/src/main/codegen/templates/DenseUnionVector.java
@@ -239,6 +239,10 @@ public class DenseUnionVector implements FieldVector {
     return internalStruct.addOrGet(fieldName(typeId, minorType), fieldType(minorType), c);
   }
 
+  private <T extends FieldVector> T addOrGet(byte typeId, MinorType minorType, ArrowType arrowType, Class<T> c) {
+    return internalStruct.addOrGet(fieldName(typeId, minorType), FieldType.nullable(arrowType), c);
+  }
+
   @Override
   public long getOffsetBufferAddress() {
     return offsetBuffer.memoryAddress();
@@ -285,13 +289,13 @@ public class DenseUnionVector implements FieldVector {
       <#assign fields = minor.fields!type.fields />
       <#assign uncappedName = name?uncap_first/>
       <#assign lowerCaseName = name?lower_case/>
-      <#if !minor.typeParams?? >
+      <#if !minor.typeParams?? || minor.class == "Decimal">
 
-  public ${name}Vector get${name}Vector(byte typeId) {
+  public ${name}Vector get${name}Vector(byte typeId<#if minor.class == "Decimal">, ArrowType arrowType</#if>) {
     ValueVector vector = childVectors[typeId];
     if (vector == null) {
       int vectorCount = internalStruct.size();
-      vector = addOrGet(typeId, MinorType.${name?upper_case}, ${name}Vector.class);
+      vector = addOrGet(typeId, MinorType.${name?upper_case}<#if minor.class == "Decimal">, arrowType</#if>, ${name}Vector.class);
       childVectors[typeId] = vector;
       if (internalStruct.size() > vectorCount) {
         vector.allocateNew();
@@ -820,7 +824,7 @@ public class DenseUnionVector implements FieldVector {
           <#assign name = minor.class?cap_first />
           <#assign fields = minor.fields!type.fields />
           <#assign uncappedName = name?uncap_first/>
-          <#if !minor.typeParams?? >
+          <#if !minor.typeParams?? || minor.class == "Decimal">
       case ${name?upper_case}:
       Nullable${name}Holder ${uncappedName}Holder = new Nullable${name}Holder();
       reader.read(${uncappedName}Holder);
@@ -844,7 +848,7 @@ public class DenseUnionVector implements FieldVector {
         <#assign name = minor.class?cap_first />
         <#assign fields = minor.fields!type.fields />
         <#assign uncappedName = name?uncap_first/>
-        <#if !minor.typeParams?? >
+        <#if !minor.typeParams?? || minor.class == "Decimal">
   public void setSafe(int index, Nullable${name}Holder holder) {
     while (index >= getOffsetBufferValueCapacity()) {
       reallocOffsetBuffer();
@@ -854,7 +858,7 @@ public class DenseUnionVector implements FieldVector {
     }
     BitVectorHelper.setBit(validityBuffer, index);
     byte typeId = getTypeId(index);
-    ${name}Vector vector = get${name}Vector(typeId);
+    ${name}Vector vector = get${name}Vector(typeId<#if minor.class == "Decimal">, new ArrowType.Decimal(holder.precision, holder.scale)</#if>);
     int offset = vector.getValueCount();
     vector.setValueCount(offset + 1);
     vector.setSafe(offset, holder);
diff --git a/java/vector/src/main/codegen/templates/DenseUnionWriter.java b/java/vector/src/main/codegen/templates/DenseUnionWriter.java
index 41c1c49..ee6f614 100644
--- a/java/vector/src/main/codegen/templates/DenseUnionWriter.java
+++ b/java/vector/src/main/codegen/templates/DenseUnionWriter.java
@@ -123,7 +123,7 @@ public class DenseUnionWriter extends AbstractFieldWriter implements FieldWriter
         <#assign name = minor.class?cap_first />
         <#assign fields = minor.fields!type.fields />
         <#assign uncappedName = name?uncap_first/>
-        <#if !minor.typeParams??>
+        <#if !minor.typeParams?? || minor.class == "Decimal">
       case ${name?upper_case}:
       return get${name}Writer(typeId);
         </#if>
@@ -138,7 +138,7 @@ public class DenseUnionWriter extends AbstractFieldWriter implements FieldWriter
       <#assign name = minor.class?cap_first />
       <#assign fields = minor.fields!type.fields />
       <#assign uncappedName = name?uncap_first/>
-      <#if !minor.typeParams?? >
+      <#if !minor.typeParams?? || minor.class == "Decimal">
 
   private ${name}Writer get${name}Writer(byte typeId) {
     ${name}Writer writer = (${name}Writer) writers[typeId];
@@ -159,10 +159,10 @@ public class DenseUnionWriter extends AbstractFieldWriter implements FieldWriter
     throw new UnsupportedOperationException();
   }
 
-  public void write${minor.class}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, </#if></#list>, byte typeId) {
+  public void write${minor.class}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, </#if></#list>, byte typeId<#if minor.class == "Decimal">, ArrowType arrowType</#if>) {
     data.setTypeId(idx(), typeId);
     get${name}Writer(typeId).setPosition(data.getOffset(idx()));
-    get${name}Writer(typeId).write${name}(<#list fields as field>${field.name}<#if field_has_next>, </#if></#list>);
+    get${name}Writer(typeId).write${name}(<#list fields as field>${field.name}<#if field_has_next>, </#if></#list><#if minor.class == "Decimal">, arrowType</#if>);
   }
       </#if>
     </#list>
@@ -208,7 +208,7 @@ public class DenseUnionWriter extends AbstractFieldWriter implements FieldWriter
   <#if lowerName == "int" ><#assign lowerName = "integer" /></#if>
   <#assign upperName = minor.class?upper_case />
   <#assign capName = minor.class?cap_first />
-  <#if !minor.typeParams?? >
+  <#if !minor.typeParams?? || minor.class == "Decimal" >
   @Override
   public ${capName}Writer ${lowerName}(String name) {
     byte typeId = data.getTypeId(idx());
@@ -225,6 +225,14 @@ public class DenseUnionWriter extends AbstractFieldWriter implements FieldWriter
     return getListWriter(typeId).${lowerName}();
   }
   </#if>
+  <#if minor.class == "Decimal">
+  public ${capName}Writer ${lowerName}(String name<#list minor.typeParams as typeParam>, ${typeParam.type} ${typeParam.name}</#list>) {
+    byte typeId = data.getTypeId(idx());
+    data.setTypeId(idx(), typeId);
+    getStructWriter(typeId).setPosition(data.getOffset(idx()));
+    return getStructWriter(typeId).${lowerName}(name<#list minor.typeParams as typeParam>, ${typeParam.name}</#list>);
+  }
+  </#if>
   </#list></#list>
 
   @Override
diff --git a/java/vector/src/main/codegen/templates/StructWriters.java b/java/vector/src/main/codegen/templates/StructWriters.java
index 8861a41..9b88924 100644
--- a/java/vector/src/main/codegen/templates/StructWriters.java
+++ b/java/vector/src/main/codegen/templates/StructWriters.java
@@ -265,7 +265,7 @@ public class ${mode}StructWriter extends AbstractFieldWriter {
     } else {
       if (writer instanceof PromotableWriter) {
         // ensure writers are initialized
-        ((PromotableWriter)writer).getWriter(MinorType.${upperName});
+        ((PromotableWriter)writer).getWriter(MinorType.${upperName}<#if minor.class == "Decimal">, new ${minor.arrowType}(precision, scale)</#if>);
       }
     }
     return writer;
diff --git a/java/vector/src/main/codegen/templates/UnionFixedSizeListWriter.java b/java/vector/src/main/codegen/templates/UnionFixedSizeListWriter.java
index 64ce120..ef6fd0c 100644
--- a/java/vector/src/main/codegen/templates/UnionFixedSizeListWriter.java
+++ b/java/vector/src/main/codegen/templates/UnionFixedSizeListWriter.java
@@ -181,11 +181,11 @@ public class UnionFixedSizeListWriter extends AbstractFieldWriter {
     writer.setPosition(writer.idx() + 1);
   }
 
-  public void writeDecimal(int start, ArrowBuf buffer) {
+  public void writeDecimal(int start, ArrowBuf buffer, ArrowType arrowType) {
     if (writer.idx() >= (idx() + 1) * listSize) {
       throw new IllegalStateException(String.format("values at index %s is greater than listSize %s", idx(), listSize));
     }
-    writer.writeDecimal(start, buffer);
+    writer.writeDecimal(start, buffer, arrowType);
     writer.setPosition(writer.idx() + 1);
   }
 
diff --git a/java/vector/src/main/codegen/templates/UnionListWriter.java b/java/vector/src/main/codegen/templates/UnionListWriter.java
index 810dc05..16c1f3b 100644
--- a/java/vector/src/main/codegen/templates/UnionListWriter.java
+++ b/java/vector/src/main/codegen/templates/UnionListWriter.java
@@ -178,6 +178,11 @@ public class UnionListWriter extends AbstractFieldWriter {
     writer.setPosition(writer.idx()+1);
   }
 
+  public void writeDecimal(int start, ArrowBuf buffer, ArrowType arrowType) {
+    writer.writeDecimal(start, buffer, arrowType);
+    writer.setPosition(writer.idx()+1);
+  }
+
   public void writeDecimal(int start, ArrowBuf buffer) {
     writer.writeDecimal(start, buffer);
     writer.setPosition(writer.idx()+1);
diff --git a/java/vector/src/main/codegen/templates/UnionReader.java b/java/vector/src/main/codegen/templates/UnionReader.java
index bfb6f19..20fdb41 100644
--- a/java/vector/src/main/codegen/templates/UnionReader.java
+++ b/java/vector/src/main/codegen/templates/UnionReader.java
@@ -88,7 +88,7 @@ public class UnionReader extends AbstractFieldReader {
       <#list type.minor as minor>
         <#assign name = minor.class?cap_first />
         <#assign uncappedName = name?uncap_first/>
-        <#if !minor.typeParams?? >
+        <#if !minor.typeParams?? || minor.class == "Decimal">
     case ${name?upper_case}:
       return (FieldReader) get${name}();
         </#if>
@@ -157,7 +157,7 @@ public class UnionReader extends AbstractFieldReader {
       <#assign friendlyType = (minor.friendlyType!minor.boxedType!type.boxedType) />
       <#assign safeType=friendlyType />
       <#if safeType=="byte[]"><#assign safeType="ByteArray" /></#if>
-      <#if !minor.typeParams?? >
+      <#if !minor.typeParams?? || minor.class == "Decimal" >
 
   private ${name}ReaderImpl ${uncappedName}Reader;
 
diff --git a/java/vector/src/main/codegen/templates/UnionVector.java b/java/vector/src/main/codegen/templates/UnionVector.java
index 8bf5d8a..177ef33 100644
--- a/java/vector/src/main/codegen/templates/UnionVector.java
+++ b/java/vector/src/main/codegen/templates/UnionVector.java
@@ -20,6 +20,7 @@ import org.apache.arrow.memory.BufferAllocator;
 import org.apache.arrow.memory.ReferenceManager;
 import org.apache.arrow.memory.util.hash.ArrowBufHasher;
 import org.apache.arrow.util.Preconditions;
+import org.apache.arrow.vector.FieldVector;
 import org.apache.arrow.vector.ValueVector;
 import org.apache.arrow.vector.complex.StructVector;
 import org.apache.arrow.vector.compare.VectorVisitor;
@@ -188,6 +189,10 @@ public class UnionVector implements FieldVector {
     return internalStruct.addOrGet(fieldName(minorType), fieldType(minorType), c);
   }
 
+  private <T extends FieldVector> T addOrGet(MinorType minorType, ArrowType arrowType, Class<T> c) {
+    return internalStruct.addOrGet(fieldName(minorType), FieldType.nullable(arrowType), c);
+  }
+
   @Override
   public long getValidityBufferAddress() {
     return typeBuffer.memoryAddress();
@@ -231,14 +236,14 @@ public class UnionVector implements FieldVector {
       <#assign fields = minor.fields!type.fields />
       <#assign uncappedName = name?uncap_first/>
       <#assign lowerCaseName = name?lower_case/>
-      <#if !minor.typeParams?? >
+      <#if !minor.typeParams?? || minor.class == "Decimal" >
 
   private ${name}Vector ${uncappedName}Vector;
 
-  public ${name}Vector get${name}Vector() {
+  public ${name}Vector get${name}Vector(<#if minor.class == "Decimal">ArrowType arrowType</#if>) {
     if (${uncappedName}Vector == null) {
       int vectorCount = internalStruct.size();
-      ${uncappedName}Vector = addOrGet(MinorType.${name?upper_case}, ${name}Vector.class);
+      ${uncappedName}Vector = addOrGet(MinorType.${name?upper_case},<#if minor.class == "Decimal"> arrowType,</#if> ${name}Vector.class);
       if (internalStruct.size() > vectorCount) {
         ${uncappedName}Vector.allocateNew();
         if (callBack != null) {
@@ -248,6 +253,14 @@ public class UnionVector implements FieldVector {
     }
     return ${uncappedName}Vector;
   }
+  <#if minor.class == "Decimal">
+  public ${name}Vector get${name}Vector() {
+    if (${uncappedName}Vector == null) {
+      throw new IllegalArgumentException("No Decimal Vector present. Provide ArrowType argument to create a new vector");
+    }
+    return ${uncappedName}Vector;
+  }
+  </#if>
       </#if>
     </#list>
   </#list>
@@ -554,11 +567,19 @@ public class UnionVector implements FieldVector {
   }
 
   public ValueVector getVector(int index) {
+    return getVector(index, null);
+  }
+
+  public ValueVector getVector(int index, ArrowType arrowType) {
     int type = typeBuffer.getByte(index * TYPE_WIDTH);
-    return getVectorByType(type);
+    return getVectorByType(type, arrowType);
   }
 
-    public ValueVector getVectorByType(int typeId) {
+  public ValueVector getVectorByType(int typeId) {
+    return getVectorByType(typeId, null);
+  }
+
+    public ValueVector getVectorByType(int typeId, ArrowType arrowType) {
       switch (MinorType.values()[typeId]) {
         case NULL:
           return null;
@@ -567,9 +588,9 @@ public class UnionVector implements FieldVector {
           <#assign name = minor.class?cap_first />
           <#assign fields = minor.fields!type.fields />
           <#assign uncappedName = name?uncap_first/>
-          <#if !minor.typeParams?? >
+          <#if !minor.typeParams?? || minor.class == "Decimal" >
         case ${name?upper_case}:
-        return get${name}Vector();
+        return get${name}Vector(<#if minor.class == "Decimal">arrowType</#if>);
           </#if>
         </#list>
       </#list>
@@ -641,6 +662,10 @@ public class UnionVector implements FieldVector {
     }
 
     public void setSafe(int index, UnionHolder holder) {
+      setSafe(index, holder, null);
+    }
+
+    public void setSafe(int index, UnionHolder holder, ArrowType arrowType) {
       FieldReader reader = holder.reader;
       if (writer == null) {
         writer = new UnionWriter(UnionVector.this);
@@ -653,11 +678,11 @@ public class UnionVector implements FieldVector {
           <#assign name = minor.class?cap_first />
           <#assign fields = minor.fields!type.fields />
           <#assign uncappedName = name?uncap_first/>
-          <#if !minor.typeParams?? >
+          <#if !minor.typeParams?? || minor.class == "Decimal" >
       case ${name?upper_case}:
         Nullable${name}Holder ${uncappedName}Holder = new Nullable${name}Holder();
         reader.read(${uncappedName}Holder);
-        setSafe(index, ${uncappedName}Holder);
+        setSafe(index, ${uncappedName}Holder<#if minor.class == "Decimal">, arrowType</#if>);
         break;
           </#if>
         </#list>
@@ -679,10 +704,10 @@ public class UnionVector implements FieldVector {
         <#assign name = minor.class?cap_first />
         <#assign fields = minor.fields!type.fields />
         <#assign uncappedName = name?uncap_first/>
-        <#if !minor.typeParams?? >
-    public void setSafe(int index, Nullable${name}Holder holder) {
+        <#if !minor.typeParams?? || minor.class == "Decimal" >
+    public void setSafe(int index, Nullable${name}Holder holder<#if minor.class == "Decimal">, ArrowType arrowType</#if>) {
       setType(index, MinorType.${name?upper_case});
-      get${name}Vector().setSafe(index, holder);
+      get${name}Vector(<#if minor.class == "Decimal">arrowType</#if>).setSafe(index, holder);
     }
 
         </#if>
diff --git a/java/vector/src/main/codegen/templates/UnionWriter.java b/java/vector/src/main/codegen/templates/UnionWriter.java
index 82e7f6d..0aa2347 100644
--- a/java/vector/src/main/codegen/templates/UnionWriter.java
+++ b/java/vector/src/main/codegen/templates/UnionWriter.java
@@ -16,6 +16,7 @@
  */
 
 import org.apache.arrow.vector.complex.impl.NullableStructWriterFactory;
+import org.apache.arrow.vector.types.Types;
 
 <@pp.dropOutputFile />
 <@pp.changeOutputFile name="/org/apache/arrow/vector/complex/impl/UnionWriter.java" />
@@ -110,6 +111,10 @@ public class UnionWriter extends AbstractFieldWriter implements FieldWriter {
   }
 
   BaseWriter getWriter(MinorType minorType) {
+    return getWriter(minorType, null);
+  }
+
+  BaseWriter getWriter(MinorType minorType, ArrowType arrowType) {
     switch (minorType) {
     case STRUCT:
       return getStructWriter();
@@ -120,9 +125,9 @@ public class UnionWriter extends AbstractFieldWriter implements FieldWriter {
         <#assign name = minor.class?cap_first />
         <#assign fields = minor.fields!type.fields />
         <#assign uncappedName = name?uncap_first/>
-        <#if !minor.typeParams??>
+        <#if !minor.typeParams?? || minor.class == "Decimal">
     case ${name?upper_case}:
-      return get${name}Writer();
+      return get${name}Writer(<#if minor.class == "Decimal" >arrowType</#if>);
         </#if>
       </#list>
     </#list>
@@ -135,36 +140,46 @@ public class UnionWriter extends AbstractFieldWriter implements FieldWriter {
       <#assign name = minor.class?cap_first />
       <#assign fields = minor.fields!type.fields />
       <#assign uncappedName = name?uncap_first/>
-      <#if !minor.typeParams?? >
+      <#assign friendlyType = (minor.friendlyType!minor.boxedType!type.boxedType) />
+      <#if !minor.typeParams?? || minor.class == "Decimal" >
 
   private ${name}Writer ${name?uncap_first}Writer;
 
-  private ${name}Writer get${name}Writer() {
+  private ${name}Writer get${name}Writer(<#if minor.class == "Decimal">ArrowType arrowType</#if>) {
     if (${uncappedName}Writer == null) {
-      ${uncappedName}Writer = new ${name}WriterImpl(data.get${name}Vector());
+      ${uncappedName}Writer = new ${name}WriterImpl(data.get${name}Vector(<#if minor.class == "Decimal">arrowType</#if>));
       ${uncappedName}Writer.setPosition(idx());
       writers.add(${uncappedName}Writer);
     }
     return ${uncappedName}Writer;
   }
 
-  public ${name}Writer as${name}() {
+  public ${name}Writer as${name}(<#if minor.class == "Decimal">ArrowType arrowType</#if>) {
     data.setType(idx(), MinorType.${name?upper_case});
-    return get${name}Writer();
+    return get${name}Writer(<#if minor.class == "Decimal">arrowType</#if>);
   }
 
   @Override
   public void write(${name}Holder holder) {
     data.setType(idx(), MinorType.${name?upper_case});
-    get${name}Writer().setPosition(idx());
-    get${name}Writer().write${name}(<#list fields as field>holder.${field.name}<#if field_has_next>, </#if></#list>);
+    <#if minor.class == "Decimal">ArrowType arrowType = new ArrowType.Decimal(holder.precision, holder.scale);</#if>
+    get${name}Writer(<#if minor.class == "Decimal">arrowType</#if>).setPosition(idx());
+    get${name}Writer(<#if minor.class == "Decimal">arrowType</#if>).write${name}(<#list fields as field>holder.${field.name}<#if field_has_next>, </#if></#list><#if minor.class == "Decimal">, arrowType</#if>);
   }
 
-  public void write${minor.class}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, </#if></#list>) {
+  public void write${minor.class}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, </#if></#list><#if minor.class == "Decimal">, ArrowType arrowType</#if>) {
     data.setType(idx(), MinorType.${name?upper_case});
-    get${name}Writer().setPosition(idx());
-    get${name}Writer().write${name}(<#list fields as field>${field.name}<#if field_has_next>, </#if></#list>);
+    get${name}Writer(<#if minor.class == "Decimal">arrowType</#if>).setPosition(idx());
+    get${name}Writer(<#if minor.class == "Decimal">arrowType</#if>).write${name}(<#list fields as field>${field.name}<#if field_has_next>, </#if></#list><#if minor.class == "Decimal">, arrowType</#if>);
+  }
+  <#if minor.class == "Decimal">
+  public void write${minor.class}(${friendlyType} value) {
+    data.setType(idx(), MinorType.DECIMAL);
+    ArrowType arrowType = new ArrowType.Decimal(value.precision(), value.scale());
+    getDecimalWriter(arrowType).setPosition(idx());
+    getDecimalWriter(arrowType).writeDecimal(value);
   }
+  </#if>
       </#if>
     </#list>
   </#list>
@@ -205,7 +220,7 @@ public class UnionWriter extends AbstractFieldWriter implements FieldWriter {
   <#if lowerName == "int" ><#assign lowerName = "integer" /></#if>
   <#assign upperName = minor.class?upper_case />
   <#assign capName = minor.class?cap_first />
-  <#if !minor.typeParams?? >
+  <#if !minor.typeParams?? || minor.class == "Decimal" >
   @Override
   public ${capName}Writer ${lowerName}(String name) {
     data.setType(idx(), MinorType.STRUCT);
@@ -220,6 +235,14 @@ public class UnionWriter extends AbstractFieldWriter implements FieldWriter {
     return getListWriter().${lowerName}();
   }
   </#if>
+  <#if minor.class == "Decimal">
+  @Override
+  public ${capName}Writer ${lowerName}(String name<#list minor.typeParams as typeParam>, ${typeParam.type} ${typeParam.name}</#list>) {
+    data.setType(idx(), MinorType.STRUCT);
+    getStructWriter().setPosition(idx());
+    return getStructWriter().${lowerName}(name<#list minor.typeParams as typeParam>, ${typeParam.name}</#list>);
+  }
+  </#if>
   </#list></#list>
 
   @Override
diff --git a/java/vector/src/main/java/org/apache/arrow/vector/DecimalVector.java b/java/vector/src/main/java/org/apache/arrow/vector/DecimalVector.java
index 50ca53f..0dc1bdd 100644
--- a/java/vector/src/main/java/org/apache/arrow/vector/DecimalVector.java
+++ b/java/vector/src/main/java/org/apache/arrow/vector/DecimalVector.java
@@ -163,6 +163,13 @@ public final class DecimalVector extends BaseFixedWidthVector {
   }
 
   /**
+   * Return precision for the decimal value.
+   */
+  public int getPrecision() {
+    return precision;
+  }
+
+  /**
    * Return scale for the decimal value.
    */
   public int getScale() {
diff --git a/java/vector/src/main/java/org/apache/arrow/vector/complex/impl/PromotableWriter.java b/java/vector/src/main/java/org/apache/arrow/vector/complex/impl/PromotableWriter.java
index f92347d..6e8089c 100644
--- a/java/vector/src/main/java/org/apache/arrow/vector/complex/impl/PromotableWriter.java
+++ b/java/vector/src/main/java/org/apache/arrow/vector/complex/impl/PromotableWriter.java
@@ -59,7 +59,6 @@ public class PromotableWriter extends AbstractPromotableFieldWriter {
   }
 
   private MinorType type;
-  private ArrowType arrowType;
   private ValueVector vector;
   private UnionVector unionVector;
   private State state;
@@ -170,14 +169,9 @@ public class PromotableWriter extends AbstractPromotableFieldWriter {
   }
 
   private void setWriter(ValueVector v) {
-    setWriter(v, null);
-  }
-
-  private void setWriter(ValueVector v, ArrowType arrowType) {
     state = State.SINGLE;
     vector = v;
     type = v.getMinorType();
-    this.arrowType = arrowType;
     switch (type) {
       case STRUCT:
         writer = nullableStructWriterFactory.build((StructVector) vector);
@@ -211,7 +205,11 @@ public class PromotableWriter extends AbstractPromotableFieldWriter {
 
   protected FieldWriter getWriter(MinorType type, ArrowType arrowType) {
     if (state == State.UNION) {
-      ((UnionWriter) writer).getWriter(type);
+      if (type == MinorType.DECIMAL) {
+        ((UnionWriter) writer).getWriter(type, arrowType);
+      } else {
+        ((UnionWriter) writer).getWriter(type);
+      }
     } else if (state == State.UNTYPED) {
       if (type == null) {
         // ???
@@ -224,11 +222,15 @@ public class PromotableWriter extends AbstractPromotableFieldWriter {
       ValueVector v = listVector != null ? listVector.addOrGetVector(fieldType).getVector() :
           fixedListVector.addOrGetVector(fieldType).getVector();
       v.allocateNew();
-      setWriter(v, arrowType);
+      setWriter(v);
       writer.setPosition(position);
     } else if (type != this.type) {
       promoteToUnion();
-      ((UnionWriter) writer).getWriter(type);
+      if (type == MinorType.DECIMAL) {
+        ((UnionWriter) writer).getWriter(type, arrowType);
+      } else {
+        ((UnionWriter) writer).getWriter(type);
+      }
     }
     return writer;
   }
@@ -268,32 +270,24 @@ public class PromotableWriter extends AbstractPromotableFieldWriter {
 
   @Override
   public void write(DecimalHolder holder) {
-    // Infer decimal scale and precision
-    if (arrowType == null) {
-      arrowType = new ArrowType.Decimal(MAX_DECIMAL_PRECISION, holder.scale);
-    }
-
-    getWriter(MinorType.DECIMAL, arrowType).write(holder);
+    getWriter(MinorType.DECIMAL, new ArrowType.Decimal(MAX_DECIMAL_PRECISION, holder.scale)).write(holder);
   }
 
   @Override
-  public void writeDecimal(int start, ArrowBuf buffer) {
-    // Cannot infer decimal scale and precision
-    if (arrowType == null) {
-      throw new IllegalStateException("Cannot infer decimal scale and precision");
-    }
-
-    getWriter(MinorType.DECIMAL, arrowType).writeDecimal(start, buffer);
+  public void writeDecimal(int start, ArrowBuf buffer, ArrowType arrowType) {
+    getWriter(MinorType.DECIMAL, new ArrowType.Decimal(MAX_DECIMAL_PRECISION,
+        ((ArrowType.Decimal) arrowType).getScale())).writeDecimal(start, buffer, arrowType);
   }
 
   @Override
   public void writeDecimal(BigDecimal value) {
-    // Infer decimal scale and precision
-    if (arrowType == null) {
-      arrowType = new ArrowType.Decimal(MAX_DECIMAL_PRECISION, value.scale());
-    }
+    getWriter(MinorType.DECIMAL, new ArrowType.Decimal(MAX_DECIMAL_PRECISION, value.scale())).writeDecimal(value);
+  }
 
-    getWriter(MinorType.DECIMAL, arrowType).writeDecimal(value);
+  @Override
+  public void writeBigEndianBytesToDecimal(byte[] value, ArrowType arrowType) {
+    getWriter(MinorType.DECIMAL, new ArrowType.Decimal(MAX_DECIMAL_PRECISION,
+        ((ArrowType.Decimal) arrowType).getScale())).writeBigEndianBytesToDecimal(value, arrowType);
   }
 
   @Override
diff --git a/java/vector/src/main/java/org/apache/arrow/vector/util/DecimalUtility.java b/java/vector/src/main/java/org/apache/arrow/vector/util/DecimalUtility.java
index a9f7a17..2703d26 100644
--- a/java/vector/src/main/java/org/apache/arrow/vector/util/DecimalUtility.java
+++ b/java/vector/src/main/java/org/apache/arrow/vector/util/DecimalUtility.java
@@ -96,6 +96,24 @@ public class DecimalUtility {
   }
 
   /**
+   * Check that the decimal scale equals the vectorScale and that the decimal precision is
+   * less than or equal to the vectorPrecision. If not, then an UnsupportedOperationException is
+   * thrown, otherwise returns true.
+   */
+  public static boolean checkPrecisionAndScale(int decimalPrecision, int decimalScale, int vectorPrecision,
+                                               int vectorScale) {
+    if (decimalScale != vectorScale) {
+      throw new UnsupportedOperationException("BigDecimal scale must equal that in the Arrow vector: " +
+          decimalScale + " != " + vectorScale);
+    }
+    if (decimalPrecision > vectorPrecision) {
+      throw new UnsupportedOperationException("BigDecimal precision can not be greater than that in the Arrow " +
+          "vector: " + decimalPrecision + " > " + vectorPrecision);
+    }
+    return true;
+  }
+
+  /**
    * Write the given BigDecimal to the ArrowBuf at the given value index. Will throw an
    * UnsupportedOperationException if the decimal size is greater than the Decimal vector byte
    * width.
diff --git a/java/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java b/java/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java
index c5c59c9..5caa440 100644
--- a/java/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java
+++ b/java/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java
@@ -20,16 +20,23 @@ package org.apache.arrow.vector.complex.impl;
 import static org.junit.Assert.assertTrue;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+import java.math.BigDecimal;
+
 import org.apache.arrow.memory.BufferAllocator;
 import org.apache.arrow.memory.RootAllocator;
 import org.apache.arrow.vector.compare.VectorEqualsVisitor;
 import org.apache.arrow.vector.complex.FixedSizeListVector;
 import org.apache.arrow.vector.complex.ListVector;
 import org.apache.arrow.vector.complex.MapVector;
+import org.apache.arrow.vector.complex.StructVector;
 import org.apache.arrow.vector.complex.reader.FieldReader;
+import org.apache.arrow.vector.complex.writer.BaseWriter.StructWriter;
 import org.apache.arrow.vector.complex.writer.FieldWriter;
+import org.apache.arrow.vector.holders.DecimalHolder;
 import org.apache.arrow.vector.types.Types;
+import org.apache.arrow.vector.types.pojo.ArrowType;
 import org.apache.arrow.vector.types.pojo.FieldType;
+import org.apache.arrow.vector.util.DecimalUtility;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -130,8 +137,8 @@ public class TestComplexCopier {
         mapWriter.value().integer().writeInt(i);
         mapWriter.endEntry();
         mapWriter.startEntry();
-        mapWriter.key().integer().writeInt(i * 2);
-        mapWriter.value().integer().writeInt(i * 2);
+        mapWriter.key().decimal().writeDecimal(BigDecimal.valueOf(i * 2));
+        mapWriter.value().decimal().writeDecimal(BigDecimal.valueOf(i * 2));
         mapWriter.endEntry();
         mapWriter.endMap();
       }
@@ -175,9 +182,8 @@ public class TestComplexCopier {
         listWriter.list().endList();
 
         listWriter.list().startList();
-        listWriter.list().bigInt().writeBigInt(i * 4);
-        listWriter.list().bigInt().writeBigInt(i * 5);
-        listWriter.list().bigInt().writeBigInt(i * 6);
+        listWriter.list().decimal().writeDecimal(BigDecimal.valueOf(i * 4));
+        listWriter.list().decimal().writeDecimal(BigDecimal.valueOf(i * 5));
         listWriter.list().endList();
         listWriter.endList();
       }
@@ -199,4 +205,150 @@ public class TestComplexCopier {
 
     }
   }
+
+  @Test
+  public void testCopyFixedSizedListOfDecimalsVector() {
+    try (FixedSizeListVector from = FixedSizeListVector.empty("v", 3, allocator);
+         FixedSizeListVector to = FixedSizeListVector.empty("v", 3, allocator)) {
+      from.addOrGetVector(FieldType.nullable(new ArrowType.Decimal(3, 0)));
+      to.addOrGetVector(FieldType.nullable(new ArrowType.Decimal(3, 0)));
+
+      DecimalHolder holder = new DecimalHolder();
+      holder.buffer = allocator.buffer(DecimalUtility.DECIMAL_BYTE_LENGTH);
+
+      // populate from vector
+      UnionFixedSizeListWriter writer = from.getWriter();
+      for (int i = 0; i < COUNT; i++) {
+        writer.startList();
+        writer.decimal().writeDecimal(BigDecimal.valueOf(i));
+
+        DecimalUtility.writeBigDecimalToArrowBuf(new BigDecimal(i * 2), holder.buffer, 0);
+        holder.start = 0;
+        holder.scale = 0;
+        holder.precision = 3;
+        writer.decimal().write(holder);
+
+        DecimalUtility.writeBigDecimalToArrowBuf(new BigDecimal(i * 3), holder.buffer, 0);
+        writer.decimal().writeDecimal(0, holder.buffer, new ArrowType.Decimal(3, 0));
+
+        writer.endList();
+      }
+      from.setValueCount(COUNT);
+      to.setValueCount(COUNT);
+
+      // copy values
+      FieldReader in = from.getReader();
+      FieldWriter out = to.getWriter();
+      for (int i = 0; i < COUNT; i++) {
+        in.setPosition(i);
+        out.setPosition(i);
+        ComplexCopier.copy(in, out);
+      }
+
+      // validate equals
+      assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
+      holder.buffer.close();
+    }
+  }
+
+  @Test
+  public void testCopyUnionListWithDecimal() {
+    try (ListVector from = ListVector.empty("v", allocator);
+         ListVector to = ListVector.empty("v", allocator)) {
+
+      UnionListWriter listWriter = from.getWriter();
+      listWriter.allocate();
+
+      for (int i = 0; i < COUNT; i++) {
+        listWriter.setPosition(i);
+        listWriter.startList();
+
+        listWriter.decimal().writeDecimal(BigDecimal.valueOf(i * 2));
+        listWriter.integer().writeInt(i);
+
+        listWriter.endList();
+      }
+      from.setValueCount(COUNT);
+
+      // copy values
+      FieldReader in = from.getReader();
+      FieldWriter out = to.getWriter();
+      for (int i = 0; i < COUNT; i++) {
+        in.setPosition(i);
+        out.setPosition(i);
+        ComplexCopier.copy(in, out);
+      }
+
+      to.setValueCount(COUNT);
+
+      // validate equals
+      assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
+
+    }
+  }
+
+  @Test
+  public void testCopyStructVector() {
+    try (final StructVector from = StructVector.empty("v", allocator);
+         final StructVector to = StructVector.empty("v", allocator)) {
+
+      from.allocateNewSafe();
+
+      NullableStructWriter structWriter = from.getWriter();
+      for (int i = 0; i < COUNT; i++) {
+        structWriter.setPosition(i);
+        structWriter.start();
+        structWriter.integer("int").writeInt(i);
+        structWriter.decimal("dec", 0, 38).writeDecimal(BigDecimal.valueOf(i * 2));
+        StructWriter innerStructWriter = structWriter.struct("struc");
+        innerStructWriter.start();
+        innerStructWriter.integer("innerint").writeInt(i * 3);
+        innerStructWriter.decimal("innerdec", 0, 38).writeDecimal(BigDecimal.valueOf(i * 4));
+        innerStructWriter.end();
+        structWriter.end();
+      }
+
+      from.setValueCount(COUNT);
+
+      // copy values
+      FieldReader in = from.getReader();
+      FieldWriter out = to.getWriter();
+      for (int i = 0; i < COUNT; i++) {
+        in.setPosition(i);
+        out.setPosition(i);
+        ComplexCopier.copy(in, out);
+      }
+      to.setValueCount(COUNT);
+
+      // validate equals
+      assertTrue(VectorEqualsVisitor.vectorEquals(from, to));
+    }
+  }
+
+  @Test
+  public void testCopyDecimalVectorWrongScale() {
+    try (FixedSizeListVector from = FixedSizeListVector.empty("v", 3, allocator);
+         FixedSizeListVector to = FixedSizeListVector.empty("v", 3, allocator)) {
+      from.addOrGetVector(FieldType.nullable(new ArrowType.Decimal(3, 2)));
+      to.addOrGetVector(FieldType.nullable(new ArrowType.Decimal(3, 1)));
+
+      // populate from vector
+      UnionFixedSizeListWriter writer = from.getWriter();
+      for (int i = 0; i < COUNT; i++) {
+        writer.startList();
+        writer.decimal().writeDecimal(BigDecimal.valueOf(1.23));
+        writer.decimal().writeDecimal(BigDecimal.valueOf(2.45));
+        writer.endList();
+      }
+      from.setValueCount(COUNT);
+      to.setValueCount(COUNT);
+
+      // copy values
+      FieldReader in = from.getReader();
+      FieldWriter out = to.getWriter();
+      UnsupportedOperationException e = assertThrows(UnsupportedOperationException.class,
+          () -> ComplexCopier.copy(in, out));
+      assertTrue(e.getMessage().contains("BigDecimal scale must equal that in the Arrow vector: 2 != 1"));
+    }
+  }
 }