You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@drill.apache.org by dz...@apache.org on 2022/07/20 16:36:20 UTC

[drill] branch master updated: DRILL-7960: Column metadata DECIMAL precision can exceed max supported value (#2597)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new d19cb1800b DRILL-7960: Column metadata DECIMAL precision can exceed max supported value (#2597)
d19cb1800b is described below

commit d19cb1800b7240bd052c6469a765ab6c36d66d5d
Author: James Turton <91...@users.noreply.github.com>
AuthorDate: Wed Jul 20 18:36:14 2022 +0200

    DRILL-7960: Column metadata DECIMAL precision can exceed max supported value (#2597)
---
 .../java/org/apache/drill/common/types/Types.java  |  59 +++---
 .../test/java/org/apache/drill/TestUnionAll.java   | 203 +++++++++++++--------
 .../exec/store/parquet/TestVarlenDecimal.java      |  27 +++
 .../drill/exec/record/metadata/MetadataUtils.java  |   3 +
 4 files changed, 190 insertions(+), 102 deletions(-)

diff --git a/common/src/main/java/org/apache/drill/common/types/Types.java b/common/src/main/java/org/apache/drill/common/types/Types.java
index b23fd6a654..775c248c64 100644
--- a/common/src/main/java/org/apache/drill/common/types/Types.java
+++ b/common/src/main/java/org/apache/drill/common/types/Types.java
@@ -32,6 +32,7 @@ import org.apache.drill.common.types.TypeProtos.MinorType;
 import com.google.protobuf.TextFormat;
 
 public class Types {
+  private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(Types.class);
 
   public static final int MAX_VARCHAR_LENGTH = 65535;
   public static final int UNDEFINED = 0;
@@ -806,32 +807,44 @@ public class Types {
    * @return type builder
    */
   public static MajorType.Builder calculateTypePrecisionAndScale(MajorType leftType, MajorType rightType, MajorType.Builder typeBuilder) {
-    if (leftType.getMinorType().equals(rightType.getMinorType())) {
-      boolean isScalarString = Types.isScalarStringType(leftType) && Types.isScalarStringType(rightType);
-      boolean isDecimal = isDecimalType(leftType);
-
-      if (isScalarString && leftType.hasPrecision() && rightType.hasPrecision()) {
-        typeBuilder.setPrecision(Math.max(leftType.getPrecision(), rightType.getPrecision()));
+    if (!leftType.getMinorType().equals(rightType.getMinorType())) {
+      return typeBuilder;
+    }
+
+    if (Types.isScalarStringType(leftType) && leftType.hasPrecision() && rightType.hasPrecision()) {
+      return typeBuilder.setPrecision(Math.max(leftType.getPrecision(), rightType.getPrecision()));
+    }
+
+    MinorType minorType = leftType.getMinorType();
+    if (isDecimalType(leftType)) {
+      int scale = Math.max(leftType.getScale(), rightType.getScale());
+      // resulting precision should take into account resulting scale value and be calculated as
+      // sum of two components:
+      // - max integer digits number (precision - scale) for left and right;
+      // - resulting scale.
+      // So for the case of cast(9999 as decimal(4,0)) and cast(1.23 as decimal(3,2))
+      // resulting scale would be Max(0, 2) = 2 and resulting precision
+      // would be Max(4 - 0, 3 - 2) + 2 = 6.
+      // In this case, both values would fit into decimal(6, 2): 9999.00, 1.23
+      int leftNumberOfDigits = leftType.getPrecision() - leftType.getScale();
+      int rightNumberOfDigits = rightType.getPrecision() - rightType.getScale();
+      int precision = Math.max(leftNumberOfDigits, rightNumberOfDigits) + scale;
+      int maxPrecision = maxPrecision(minorType);
+
+      if (precision > maxPrecision) {
+        logger.warn(
+          "Possible loss of precision: wanted {}({}, {}) but limited to {}({}, {})",
+          minorType, precision, scale,
+          minorType, maxPrecision, scale
+        );
+        precision = maxPrecision;
       }
 
-      if (isDecimal) {
-        int scale = Math.max(leftType.getScale(), rightType.getScale());
-        // resulting precision should take into account resulting scale value and be calculated as
-        // sum of two components:
-        // - max integer digits number (precision - scale) for left and right;
-        // - resulting scale.
-        // So for the case of cast(9999 as decimal(4,0)) and cast(1.23 as decimal(3,2))
-        // resulting scale would be Max(0, 2) = 2 and resulting precision
-        // would be Max(4 - 0, 3 - 2) + 2 = 6.
-        // In this case, both values would fit into decimal(6, 2): 9999.00, 1.23
-        int leftNumberOfDigits = leftType.getPrecision() - leftType.getScale();
-        int rightNumberOfDigits = rightType.getPrecision() - rightType.getScale();
-        int precision = Math.max(leftNumberOfDigits, rightNumberOfDigits) + scale;
-
-        typeBuilder.setPrecision(precision);
-        typeBuilder.setScale(scale);
-      }
+      typeBuilder.setPrecision(precision);
+      typeBuilder.setScale(scale);
+      return typeBuilder;
     }
+
     return typeBuilder;
   }
 
diff --git a/exec/java-exec/src/test/java/org/apache/drill/TestUnionAll.java b/exec/java-exec/src/test/java/org/apache/drill/TestUnionAll.java
index 5a0047369d..5d0864ea25 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/TestUnionAll.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/TestUnionAll.java
@@ -27,11 +27,14 @@ import org.apache.drill.categories.UnlikelyTest;
 import org.apache.drill.common.exceptions.UserException;
 import org.apache.drill.common.expression.SchemaPath;
 import org.apache.drill.common.types.TypeProtos;
+import org.apache.drill.exec.ExecConstants;
+import org.apache.drill.exec.planner.physical.PlannerSettings;
 import org.apache.drill.exec.record.BatchSchema;
 import org.apache.drill.exec.record.metadata.SchemaBuilder;
 import org.apache.drill.exec.work.foreman.SqlUnsupportedException;
 import org.apache.drill.exec.work.foreman.UnsupportedRelOperatorException;
-import org.apache.drill.test.BaseTestQuery;
+import org.apache.drill.test.ClusterFixture;
+import org.apache.drill.test.ClusterTest;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
@@ -43,17 +46,14 @@ import java.nio.file.Paths;
 import java.util.List;
 
 @Category({SqlTest.class, OperatorTest.class})
-public class TestUnionAll extends BaseTestQuery {
-
-  private static final String sliceTargetSmall = "alter session set `planner.slice_target` = 1";
-  private static final String sliceTargetDefault = "alter session reset `planner.slice_target`";
-  private static final String enableDistribute = "alter session set `planner.enable_unionall_distribute` = true";
-  private static final String defaultDistribute = "alter session reset `planner.enable_unionall_distribute`";
+public class TestUnionAll extends ClusterTest {
 
+  private static final String SLICE_TARGET_DEFAULT = "alter session reset `planner.slice_target`";
   private static final String EMPTY_DIR_NAME = "empty_directory";
 
   @BeforeClass
-  public static void setupTestFiles() {
+  public static void setupTestFiles() throws Exception {
+    startCluster(ClusterFixture.builder(dirTestWatcher));
     dirTestWatcher.copyResourceToRoot(Paths.get("multilevel", "parquet"));
     dirTestWatcher.makeTestTmpSubDir(Paths.get(EMPTY_DIR_NAME));
   }
@@ -217,9 +217,9 @@ public class TestUnionAll extends BaseTestQuery {
   @Category(UnlikelyTest.class)
   public void testUnionAllViewExpandableStar() throws Exception {
     try {
-      test("use dfs.tmp");
-      test("create view nation_view_testunionall_expandable_star as select n_name, n_nationkey from cp.`tpch/nation.parquet`;");
-      test("create view region_view_testunionall_expandable_star as select r_name, r_regionkey from cp.`tpch/region.parquet`;");
+      run("use dfs.tmp");
+      run("create view nation_view_testunionall_expandable_star as select n_name, n_nationkey from cp.`tpch/nation.parquet`");
+      run("create view region_view_testunionall_expandable_star as select r_name, r_regionkey from cp.`tpch/region.parquet`");
 
       String query1 = "(select * from dfs.tmp.`nation_view_testunionall_expandable_star`) " +
           "union all " +
@@ -245,34 +245,34 @@ public class TestUnionAll extends BaseTestQuery {
           .baselineColumns("r_name", "r_regionkey")
           .build().run();
     } finally {
-      test("drop view if exists nation_view_testunionall_expandable_star");
-      test("drop view if exists region_view_testunionall_expandable_star");
+      run("drop view if exists nation_view_testunionall_expandable_star");
+      run("drop view if exists region_view_testunionall_expandable_star");
     }
   }
 
   @Test(expected = UnsupportedRelOperatorException.class) // see DRILL-2002
   public void testUnionAllViewUnExpandableStar() throws Exception {
     try {
-      test("use dfs.tmp");
-      test("create view nation_view_testunionall_expandable_star as select * from cp.`tpch/nation.parquet`;");
+      run("use dfs.tmp");
+      run("create view nation_view_testunionall_expandable_star as select * from cp.`tpch/nation.parquet`");
 
       String query = "(select * from dfs.tmp.`nation_view_testunionall_expandable_star`) " +
                      "union all (select * from cp.`tpch/region.parquet`)";
-      test(query);
+      run(query);
     } catch(UserException ex) {
       SqlUnsupportedException.errorClassNameToException(ex.getOrCreatePBError(false).getException().getExceptionClass());
       throw ex;
     } finally {
-      test("drop view if exists nation_view_testunionall_expandable_star");
+      run("drop view if exists nation_view_testunionall_expandable_star");
     }
   }
 
   @Test
   public void testDiffDataTypesAndModes() throws Exception {
     try {
-      test("use dfs.tmp");
-      test("create view nation_view_testunionall_expandable_star as select n_name, n_nationkey from cp.`tpch/nation.parquet`;");
-      test("create view region_view_testunionall_expandable_star as select r_name, r_regionkey from cp.`tpch/region.parquet`;");
+      run("use dfs.tmp");
+      run("create view nation_view_testunionall_expandable_star as select n_name, n_nationkey from cp.`tpch/nation.parquet`");
+      run("create view region_view_testunionall_expandable_star as select r_name, r_regionkey from cp.`tpch/region.parquet`");
 
       String t1 = "(select n_comment, n_regionkey from cp.`tpch/nation.parquet` limit 5)";
       String t2 = "(select * from nation_view_testunionall_expandable_star  limit 5)";
@@ -289,8 +289,8 @@ public class TestUnionAll extends BaseTestQuery {
           .baselineColumns("n_comment", "n_regionkey")
           .build().run();
     } finally {
-      test("drop view if exists nation_view_testunionall_expandable_star");
-      test("drop view if exists region_view_testunionall_expandable_star");
+      run("drop view if exists nation_view_testunionall_expandable_star");
+      run("drop view if exists region_view_testunionall_expandable_star");
     }
   }
 
@@ -389,7 +389,7 @@ public class TestUnionAll extends BaseTestQuery {
     String rootInt = "/store/json/intData.json";
     String rootBoolean = "/store/json/booleanData.json";
 
-    test("(select key from cp.`%s` " +
+    run("(select key from cp.`%s` " +
         "union all " +
         "select key from cp.`%s` )", rootInt, rootBoolean);
   }
@@ -643,8 +643,11 @@ public class TestUnionAll extends BaseTestQuery {
             ".*SelectionVectorRemover.*\n" +
                 ".*Filter.*\n" +
                     ".*Scan.*columns=\\[`r_regionkey`\\].*"};
-    final String[] excludedPlan = {};
-    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan);
+    queryBuilder()
+      .sql(query)
+      .planMatcher()
+      .include(expectedPlan)
+      .match();
 
     // Validate the result
     testBuilder()
@@ -685,8 +688,11 @@ public class TestUnionAll extends BaseTestQuery {
                         ".*Filter.*\n" +
                             ".*Scan.*columns=\\[`n_regionkey`, `n_nationkey`\\].*\n" +
                         ".*Scan.*columns=\\[`r_regionkey`\\].*"};
-    final String[] excludedPlan = {};
-    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan);
+    queryBuilder()
+      .sql(query)
+      .planMatcher()
+      .include(expectedPlan)
+      .match();
 
     // Validate the result
     testBuilder()
@@ -724,8 +730,11 @@ public class TestUnionAll extends BaseTestQuery {
                     ".*Project.*\n" +
                         ".*Scan.*columns=\\[`columns`\\[0\\]\\].*"};
 
-    final String[] excludedPlan = {};
-    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan);
+    queryBuilder()
+      .sql(query)
+      .planMatcher()
+      .include(expectedPlan)
+      .match();
 
     // Validate the result
     testBuilder()
@@ -752,8 +761,12 @@ public class TestUnionAll extends BaseTestQuery {
             ".*Project.*\n" +
                 ".*Scan.*columns=\\[`r_regionkey`, `r_name`\\].*"
     };
-    final String[] excludedPlan = {};
-    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan);
+
+    queryBuilder()
+      .sql(query)
+      .planMatcher()
+      .include(expectedPlan)
+      .match();
 
     testBuilder()
         .sqlQuery(query)
@@ -776,8 +789,12 @@ public class TestUnionAll extends BaseTestQuery {
         ".*UnionAll.*\n" +
             ".*Scan.*columns=\\[`n_nationkey`\\].*\n" +
             ".*Scan.*columns=\\[`r_regionkey`\\].*"};
-    final String[] excludedPlan = {};
-    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan);
+
+    queryBuilder()
+      .sql(query)
+      .planMatcher()
+      .include(expectedPlan)
+      .match();
 
     // Validate the result
     testBuilder()
@@ -802,8 +819,12 @@ public class TestUnionAll extends BaseTestQuery {
             ".*Scan.*columns=\\[`n_nationkey`\\].*\n" +
         ".*Project\\(col=\\[\\*\\(2, \\$0\\)\\]\\).*\n" +
             ".*Scan.*columns=\\[`r_regionkey`\\].*"};
-    final String[] excludedPlan = {};
-    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan);
+
+    queryBuilder()
+      .sql(query)
+      .planMatcher()
+      .include(expectedPlan)
+      .match();
 
     // Validate the result
     testBuilder()
@@ -830,8 +851,12 @@ public class TestUnionAll extends BaseTestQuery {
             ".*Scan.*columns=\\[`n_nationkey`\\].*\n" +
         ".*Project\\(col=\\[\\*\\(2, ITEM\\(\\$0, 0\\)\\)\\]\\).*\n" +
             ".*Scan.*columns=\\[`columns`\\[0\\]\\]"};
-    final String[] excludedPlan = {};
-    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan);
+
+    queryBuilder()
+      .sql(query)
+      .planMatcher()
+      .include(expectedPlan)
+      .match();
 
     // Validate the result
     testBuilder()
@@ -856,8 +881,12 @@ public class TestUnionAll extends BaseTestQuery {
             ".*Scan.*columns=\\[`n_comment`, `n_nationkey`, `n_name`\\].*\n" +
         ".*Project.*\n" +
             ".*Scan.*columns=\\[`r_comment`, `r_regionkey`, `r_name`\\]"};
-    final String[] excludedPlan = {};
-    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan);
+
+    queryBuilder()
+      .sql(query)
+      .planMatcher()
+      .include(expectedPlan)
+      .match();
 
     // Validate the result
     testBuilder()
@@ -887,8 +916,12 @@ public class TestUnionAll extends BaseTestQuery {
             ".*Filter.*\n" +
                 ".*Scan.*columns=\\[`r_regionkey`\\]"
     };
-    final String[] excludedPlan = {};
-    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan);
+
+    queryBuilder()
+      .sql(query)
+      .planMatcher()
+      .include(expectedPlan)
+      .match();
 
     // Validate the result
     testBuilder()
@@ -1028,22 +1061,24 @@ public class TestUnionAll extends BaseTestQuery {
     // Validate the plan
     final String[] expectedPlan = {"UnionExchange.*\n",
         ".*UnionAll"};
-    final String[] excludedPlan = {};
 
     try {
-      test(sliceTargetSmall);
-      PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan);
+      client.alterSession(ExecConstants.SLICE_TARGET, 1);
+      queryBuilder()
+        .sql(query)
+        .planMatcher()
+        .include(expectedPlan)
+        .match();
 
       testBuilder()
-        .optionSettingQueriesForTestQuery(sliceTargetSmall)
-        .optionSettingQueriesForBaseline(sliceTargetDefault)
+        .optionSettingQueriesForBaseline(SLICE_TARGET_DEFAULT)
         .unOrdered()
         .sqlQuery(query)
         .sqlBaselineQuery(query)
         .build()
         .run();
     } finally {
-      test(sliceTargetDefault);
+      client.resetSession(ExecConstants.SLICE_TARGET);
     }
   }
 
@@ -1059,22 +1094,24 @@ public class TestUnionAll extends BaseTestQuery {
 
     // Validate the plan
     final String[] expectedPlan = {"(?s)UnionExchange.*HashAgg.*HashToRandomExchange.*UnionAll.*"};
-    final String[] excludedPlan = {};
 
     try {
-      test(sliceTargetSmall);
-      PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan);
+      client.alterSession(ExecConstants.SLICE_TARGET, 1);
+      queryBuilder()
+        .sql(query)
+        .planMatcher()
+        .include(expectedPlan)
+        .match();
 
       testBuilder()
-        .optionSettingQueriesForTestQuery(sliceTargetSmall)
-        .optionSettingQueriesForBaseline(sliceTargetDefault)
+        .optionSettingQueriesForBaseline(SLICE_TARGET_DEFAULT)
         .unOrdered()
         .sqlQuery(query)
         .sqlBaselineQuery(query)
         .build()
         .run();
     } finally {
-      test(sliceTargetDefault);
+      client.resetSession(ExecConstants.SLICE_TARGET);
     }
   }
 
@@ -1089,22 +1126,24 @@ public class TestUnionAll extends BaseTestQuery {
 
     // Validate the plan
     final String[] expectedPlan = {"(?s)UnionExchange.*UnionAll.*HashJoin.*"};
-    final String[] excludedPlan = {};
 
     try {
-      test(sliceTargetSmall);
-      PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan);
+      client.alterSession(ExecConstants.SLICE_TARGET, 1);
+      queryBuilder()
+        .sql(query)
+        .planMatcher()
+        .include(expectedPlan)
+        .match();
 
       testBuilder()
-        .optionSettingQueriesForTestQuery(sliceTargetSmall)
-        .optionSettingQueriesForBaseline(sliceTargetDefault)
+        .optionSettingQueriesForBaseline(SLICE_TARGET_DEFAULT)
         .unOrdered()
         .sqlQuery(query)
         .sqlBaselineQuery(query)
         .build()
         .run();
     } finally {
-      test(sliceTargetDefault);
+      client.resetSession(ExecConstants.SLICE_TARGET);
     }
   }
 
@@ -1120,25 +1159,26 @@ public class TestUnionAll extends BaseTestQuery {
 
     // Validate the plan
     final String[] expectedPlan = {"(?s)UnionExchange.*UnionAll.*HashJoin.*"};
-    final String[] excludedPlan = {};
 
     try {
-      test(sliceTargetSmall);
-      test(enableDistribute);
-
-      PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan);
+      client.alterSession(ExecConstants.SLICE_TARGET, 1);
+      client.alterSession(PlannerSettings.UNIONALL_DISTRIBUTE_KEY, true);
+      queryBuilder()
+        .sql(query)
+        .planMatcher()
+        .include(expectedPlan)
+        .match();
 
       testBuilder()
-        .optionSettingQueriesForTestQuery(sliceTargetSmall)
-        .optionSettingQueriesForBaseline(sliceTargetDefault)
+        .optionSettingQueriesForBaseline(SLICE_TARGET_DEFAULT)
         .unOrdered()
         .sqlQuery(query)
         .sqlBaselineQuery(query)
         .build()
         .run();
     } finally {
-      test(sliceTargetDefault);
-      test(defaultDistribute);
+      client.resetSession(ExecConstants.SLICE_TARGET);
+      client.resetSession(PlannerSettings.UNIONALL_DISTRIBUTE_KEY);
     }
   }
 
@@ -1155,25 +1195,26 @@ public class TestUnionAll extends BaseTestQuery {
 
     // Validate the plan
     final String[] expectedPlan = {"(?s)UnionExchange.*UnionAll.*HashJoin.*"};
-    final String[] excludedPlan = {};
 
     try {
-      test(sliceTargetSmall);
-      test(enableDistribute);
-
-      PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPlan);
+      client.alterSession(ExecConstants.SLICE_TARGET, 1);
+      client.alterSession(PlannerSettings.UNIONALL_DISTRIBUTE_KEY, true);
+      queryBuilder()
+        .sql(query)
+        .planMatcher()
+        .include(expectedPlan)
+        .match();
 
       testBuilder()
-        .optionSettingQueriesForTestQuery(sliceTargetSmall)
-        .optionSettingQueriesForBaseline(sliceTargetDefault)
+        .optionSettingQueriesForBaseline(SLICE_TARGET_DEFAULT)
         .unOrdered()
         .sqlQuery(query)
         .sqlBaselineQuery(query)
         .build()
         .run();
     } finally {
-      test(sliceTargetDefault);
-      test(defaultDistribute);
+      client.resetSession(ExecConstants.SLICE_TARGET);
+      client.resetSession(PlannerSettings.UNIONALL_DISTRIBUTE_KEY);
     }
   }
 
@@ -1311,7 +1352,12 @@ public class TestUnionAll extends BaseTestQuery {
                 ".*Filter.*\n" +
                     ".*Scan.*columns=\\[`r_regionkey`\\]"};
 
-    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, null);
+      queryBuilder()
+        .sql(query)
+        .planMatcher()
+        .include(expectedPlan)
+        .match();
+
 
     testBuilder()
         .sqlQuery(query)
@@ -1341,5 +1387,4 @@ public class TestUnionAll extends BaseTestQuery {
       .build()
       .run();
   }
-
 }
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/store/parquet/TestVarlenDecimal.java b/exec/java-exec/src/test/java/org/apache/drill/exec/store/parquet/TestVarlenDecimal.java
index 822497cd8a..61f8e4df87 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/exec/store/parquet/TestVarlenDecimal.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/store/parquet/TestVarlenDecimal.java
@@ -191,4 +191,31 @@ public class TestVarlenDecimal extends ClusterTest {
       run("drop table if exists dfs.tmp.t");
     }
   }
+
+  @Test // DRILL-7960
+  public void testWideningLimit() throws Exception {
+    // A union of VARDECIMALs that requires a widening to an unsupported
+    // DECIMAL(40, 6). The resulting column should be limited DECIMAL(38, 6)
+    // and a precision loss warning logged.
+    String query = "SELECT CAST(10 AS DECIMAL(38, 4)) AS `Col1` " +
+      "UNION ALL " +
+      "SELECT CAST(22 AS DECIMAL(29, 6)) AS `Col1`";
+
+    testBuilder().sqlQuery(query)
+      .unOrdered()
+      .baselineColumns("Col1")
+      .baselineValues(new BigDecimal("10.000000"))
+      .baselineValues(new BigDecimal("22.000000"))
+      .go();
+
+    List<Pair<SchemaPath, TypeProtos.MajorType>> expectedSchema = Collections.singletonList(Pair.of(
+        SchemaPath.getSimplePath("Col1"),
+        Types.withPrecisionAndScale(MinorType.VARDECIMAL, DataMode.REQUIRED, 38, 6)
+    ));
+
+    testBuilder()
+        .sqlQuery(query)
+        .schemaBaseLine(expectedSchema)
+        .go();
+  }
 }
diff --git a/exec/vector/src/main/java/org/apache/drill/exec/record/metadata/MetadataUtils.java b/exec/vector/src/main/java/org/apache/drill/exec/record/metadata/MetadataUtils.java
index 375a43d589..6df24faaa6 100644
--- a/exec/vector/src/main/java/org/apache/drill/exec/record/metadata/MetadataUtils.java
+++ b/exec/vector/src/main/java/org/apache/drill/exec/record/metadata/MetadataUtils.java
@@ -28,6 +28,9 @@ import org.apache.drill.common.types.Types;
 import org.apache.drill.exec.record.MaterializedField;
 import org.apache.drill.exec.vector.complex.DictVector;
 
+/**
+ * A collection of utility methods for working with column and tuple metadata.
+ */
 public class MetadataUtils {
 
   public static TupleSchema fromFields(Iterable<MaterializedField> fields) {