You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@drill.apache.org by vo...@apache.org on 2019/01/25 16:49:09 UTC

[drill] 05/08: DRILL-6962: Function coalesce returns an Error when none of the columns in coalesce exist in a parquet file

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

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

commit 780a3fbb0222ac037d3c559dc88c7f9dbd48b0cb
Author: Bohdan Kazydub <bo...@gmail.com>
AuthorDate: Thu Dec 20 20:58:16 2018 +0200

    DRILL-6962: Function coalesce returns an Error when none of the columns in coalesce exist in a parquet file
    
    - Updated UntypedNullVector to hold value count when vector is allocated and transfered to another one;
    - Updated RecordBatchLoader and DrillCursor to handle case when only UntypedNull values are present in RecordBatch (special case when data buffer is null but actual values are present);
    - Added functions to cast UntypedNull value to other types for use in UDFs;
    - Moved UntypedReader, UntypedHolderReaderImpl and UntypedReaderImpl from org.apache.drill.exec.vector.complex.impl to org.apache.drill.exec.vector package.
    
    closes #1614
---
 exec/java-exec/src/main/codegen/data/Casts.tdd     |  17 +++
 .../main/codegen/templates/CastUntypedNull.java    |  61 +++++++++++
 .../drill/exec/record/RecordBatchLoader.java       |   6 ++
 .../java/org/apache/drill/TestJoinNullable.java    |  19 ++++
 .../java/org/apache/drill/TestUntypedNull.java     | 119 +++++++++++++++++++++
 .../drill/exec/fn/impl/TestCastFunctions.java      |  70 ++++++++++++
 .../org/apache/drill/jdbc/impl/DrillCursor.java    |   9 +-
 .../codegen/templates/AbstractFieldReader.java     |   4 +-
 .../main/codegen/templates/BasicTypeHelper.java    |   2 +
 .../impl => }/UntypedHolderReaderImpl.java         |   5 +-
 .../drill/exec/vector/UntypedNullVector.java       |  18 ++--
 .../vector/{complex/impl => }/UntypedReader.java   |   4 +-
 .../{complex/impl => }/UntypedReaderImpl.java      |   5 +-
 .../exec/vector/complex/reader/FieldReader.java    |   6 +-
 14 files changed, 319 insertions(+), 26 deletions(-)

diff --git a/exec/java-exec/src/main/codegen/data/Casts.tdd b/exec/java-exec/src/main/codegen/data/Casts.tdd
index 31eef19..7652ab7 100644
--- a/exec/java-exec/src/main/codegen/data/Casts.tdd
+++ b/exec/java-exec/src/main/codegen/data/Casts.tdd
@@ -168,5 +168,22 @@
     {from: "NullableVarBinary", to: "NullableFloat8", major: "EmptyString", javaType:"Double", parse:"Double"},
 
     {from: "NullableVarBinary", to: "NullableVarDecimal", major: "NullableVarCharDecimalComplex"},
+
+    {from: "UntypedNull", to: "Bit", major: "UntypedNull"},
+    {from: "UntypedNull", to: "TinyInt", major: "UntypedNull"},
+    {from: "UntypedNull", to: "Int", major: "UntypedNull"},
+    {from: "UntypedNull", to: "BigInt", major: "UntypedNull"},
+    {from: "UntypedNull", to: "Float4", major: "UntypedNull"},
+    {from: "UntypedNull", to: "Float8", major: "UntypedNull"},
+    {from: "UntypedNull", to: "Date", major: "UntypedNull"},
+    {from: "UntypedNull", to: "Time", major: "UntypedNull"},
+    {from: "UntypedNull", to: "TimeStamp", major: "UntypedNull"},
+    {from: "UntypedNull", to: "Interval", major: "UntypedNull"},
+    {from: "UntypedNull", to: "IntervalDay", major: "UntypedNull"},
+    {from: "UntypedNull", to: "IntervalYear", major: "UntypedNull"},
+    {from: "UntypedNull", to: "VarBinary", major: "UntypedNull"},
+    {from: "UntypedNull", to: "VarChar", major: "UntypedNull"},
+    {from: "UntypedNull", to: "Var16Char", major: "UntypedNull"},
+    {from: "UntypedNull", to: "VarDecimal", major: "UntypedNull"}
   ]
 }
diff --git a/exec/java-exec/src/main/codegen/templates/CastUntypedNull.java b/exec/java-exec/src/main/codegen/templates/CastUntypedNull.java
new file mode 100644
index 0000000..4a22337
--- /dev/null
+++ b/exec/java-exec/src/main/codegen/templates/CastUntypedNull.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+<@pp.dropOutputFile />
+
+<#list cast.types as type>
+<#if type.major == "UntypedNull">
+
+<@pp.changeOutputFile name="/org/apache/drill/exec/expr/fn/impl/gcast/Cast${type.from}${type.to}.java" />
+
+<#include "/@includes/license.ftl" />
+package org.apache.drill.exec.expr.fn.impl.gcast;
+
+import org.apache.drill.exec.expr.DrillSimpleFunc;
+import org.apache.drill.exec.expr.annotations.FunctionTemplate;
+import org.apache.drill.exec.expr.annotations.Output;
+import org.apache.drill.exec.expr.annotations.Param;
+import org.apache.drill.exec.expr.holders.*;
+import org.apache.drill.exec.vector.UntypedNullHolder;
+
+/*
+ * This class is generated using freemarker and the ${.template_name} template.
+ */
+@FunctionTemplate(name = "cast${type.to?upper_case}",
+    scope = FunctionTemplate.FunctionScope.SIMPLE,
+    nulls = FunctionTemplate.NullHandling.NULL_IF_NULL)
+public class Cast${type.from}${type.to} implements DrillSimpleFunc {
+
+  @Param ${type.from}Holder in;
+  <#if type.to == "VarDecimal">
+  @Param IntHolder precision;
+  @Param IntHolder scale;
+  <#elseif type.to == "VarChar" || type.to == "VarBinary" || type.to == "Var16Char">
+  @Param BigIntHolder len;
+  </#if>
+  @Output Nullable${type.to}Holder out;
+
+  public void setup() {
+  }
+
+  public void eval() {
+    out.isSet = 0;
+  }
+}
+</#if> <#-- type.major -->
+</#list>
+
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/record/RecordBatchLoader.java b/exec/java-exec/src/main/java/org/apache/drill/exec/record/RecordBatchLoader.java
index 696d6db..cd3a22f 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/record/RecordBatchLoader.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/record/RecordBatchLoader.java
@@ -36,6 +36,7 @@ import org.apache.drill.exec.proto.UserBitShared.SerializedField;
 import org.apache.drill.exec.record.selection.SelectionVector2;
 import org.apache.drill.exec.record.selection.SelectionVector4;
 import org.apache.drill.exec.vector.AllocationHelper;
+import org.apache.drill.exec.vector.UntypedNullVector;
 import org.apache.drill.exec.vector.ValueVector;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -128,6 +129,11 @@ public class RecordBatchLoader implements VectorAccessible, Iterable<VectorWrapp
 
         // Load the vector.
         if (buf == null) {
+          // Buffers for untyped null vectors are always null and for the case
+          // field value alone is sufficient to load the vector
+          if (vector instanceof UntypedNullVector) {
+            vector.load(field, null);
+          }
           // Schema only
         } else if (field.getValueCount() == 0) {
           AllocationHelper.allocate(vector, 0, 0, 0);
diff --git a/exec/java-exec/src/test/java/org/apache/drill/TestJoinNullable.java b/exec/java-exec/src/test/java/org/apache/drill/TestJoinNullable.java
index 949acf3..13f5dd8 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/TestJoinNullable.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/TestJoinNullable.java
@@ -556,6 +556,25 @@ public class TestJoinNullable extends BaseTestQuery {
     }
   }
 
+  // Full join with USING clause uses COALESCE internally
+  @Test // DRILL-6962
+  public void testFullJoinUsingUntypedNullColumn() throws Exception {
+    try {
+      enableJoin(true, true);
+      String query = "select * from " +
+          "(select n_nationkey, n_name, coalesce(unk1, unk2) as not_exists from cp.`tpch/nation.parquet`) t1 full join " +
+          "(select r_name, r_comment, coalesce(unk1, unk2) as not_exists from cp.`tpch/region.parquet`) t2 " +
+          "using (not_exists)";
+      testBuilder()
+          .sqlQuery(query)
+          .unOrdered()
+          .expectsNumRecords(30)
+          .go();
+    } finally {
+      resetJoinOptions();
+    }
+  }
+
   public void nullMixedComparatorEqualJoinHelper(final String query) throws Exception {
     testBuilder()
         .sqlQuery(query)
diff --git a/exec/java-exec/src/test/java/org/apache/drill/TestUntypedNull.java b/exec/java-exec/src/test/java/org/apache/drill/TestUntypedNull.java
index 4976947..521531c 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/TestUntypedNull.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/TestUntypedNull.java
@@ -18,7 +18,11 @@
 package org.apache.drill;
 
 import org.apache.drill.categories.SqlFunctionTest;
+import org.apache.drill.common.types.TypeProtos;
+import org.apache.drill.common.types.Types;
 import org.apache.drill.exec.ExecConstants;
+import org.apache.drill.exec.record.BatchSchema;
+import org.apache.drill.exec.record.metadata.SchemaBuilder;
 import org.apache.drill.test.ClusterFixture;
 import org.apache.drill.test.ClusterFixtureBuilder;
 import org.apache.drill.test.ClusterTest;
@@ -36,6 +40,8 @@ import static org.junit.Assert.assertTrue;
 @Category(SqlFunctionTest.class)
 public class TestUntypedNull extends ClusterTest {
 
+  private static final TypeProtos.MajorType UNTYPED_NULL_TYPE = Types.optional(TypeProtos.MinorType.NULL);
+
   @BeforeClass
   public static void setup() throws Exception {
     ClusterFixtureBuilder builder = ClusterFixture.builder(dirTestWatcher);
@@ -106,5 +112,118 @@ public class TestUntypedNull extends ClusterTest {
     assertEquals(0, summary.recordCount());
   }
 
+  @Test
+  public void testCoalesceOnNotExistentColumns() throws Exception {
+    String query = "select coalesce(unk1, unk2) as coal from cp.`tpch/nation.parquet` limit 5";
+    BatchSchema expectedSchema = new SchemaBuilder()
+        .add("coal", UNTYPED_NULL_TYPE)
+        .build();
+
+    testBuilder()
+        .sqlQuery(query)
+        .schemaBaseLine(expectedSchema)
+        .go();
+
+    testBuilder()
+        .sqlQuery(query)
+        .unOrdered()
+        .baselineColumns("coal")
+        .baselineValuesForSingleColumn(null, null, null, null, null)
+        .go();
+  }
+
+  @Test
+  public void testCoalesceOnNotExistentColumnsWithGroupBy() throws Exception {
+    String query = "select coalesce(unk1, unk2) as coal from cp.`tpch/nation.parquet` group by 1";
+    BatchSchema expectedSchema = new SchemaBuilder()
+        .add("coal", UNTYPED_NULL_TYPE)
+        .build();
+
+    testBuilder()
+      .sqlQuery(query)
+        .schemaBaseLine(expectedSchema)
+        .go();
+
+    testBuilder()
+        .sqlQuery(query)
+        .unOrdered()
+        .baselineColumns("coal")
+        .baselineValuesForSingleColumn(new Object[] {null})
+        .go();
+  }
+
+  @Test
+  public void testCoalesceOnNotExistentColumnsWithOrderBy() throws Exception {
+    String query = "select coalesce(unk1, unk2) as coal from cp.`tpch/nation.parquet` order by 1 limit 5";
+    BatchSchema expectedSchema = new SchemaBuilder()
+        .add("coal", UNTYPED_NULL_TYPE)
+        .build();
+
+    testBuilder()
+        .sqlQuery(query)
+        .schemaBaseLine(expectedSchema)
+        .go();
+
+    testBuilder()
+        .sqlQuery(query)
+        .unOrdered()
+        .baselineColumns("coal")
+        .baselineValuesForSingleColumn(null, null, null, null, null)
+        .go();
+  }
+
+  @Test
+  public void testCoalesceOnNotExistentColumnsWithCoalesceInWhereClause() throws Exception {
+    String query = "select coalesce(unk1, unk2) as coal from cp.`tpch/nation.parquet` where coalesce(unk1, unk2) > 10";
+    testBuilder()
+        .sqlQuery(query)
+        .unOrdered()
+        .expectsNumRecords(0)
+        .go();
+  }
+
+  @Test
+  public void testCoalesceOnNotExistentColumnsWithCoalesceInHavingClause() throws Exception {
+    String query = "select 1 from cp.`tpch/nation.parquet` group by n_name having count(coalesce(unk1, unk2)) > 10";
+    testBuilder()
+        .sqlQuery(query)
+        .unOrdered()
+        .expectsNumRecords(0)
+        .go();
+  }
+
+  @Test
+  public void testPartitionByCoalesceOnNotExistentColumns() throws Exception {
+    String query =
+        "select row_number() over (partition by coalesce(unk1, unk2)) as row_num from cp.`tpch/nation.parquet` limit 5";
+    testBuilder()
+        .sqlQuery(query)
+        .unOrdered()
+        .baselineColumns("row_num")
+        .baselineValuesForSingleColumn(1L, 2L, 3L, 4L, 5L)
+        .go();
+  }
+
+  @Test
+  public void testCoalesceOnNotExistentColumnsInUDF() throws Exception {
+    String query = "select substr(coalesce(unk1, unk2), 1, 2) as coal from cp.`tpch/nation.parquet` limit 5";
+    testBuilder()
+        .sqlQuery(query)
+        .unOrdered()
+        .baselineColumns("coal")
+        .baselineValuesForSingleColumn(null, null, null, null, null)
+        .go();
+  }
+
+  @Test
+  public void testCoalesceOnNotExistentColumnsInUDF2() throws Exception {
+    String query = "select abs(coalesce(unk1, unk2)) as coal from cp.`tpch/nation.parquet` limit 5";
+    testBuilder()
+        .sqlQuery(query)
+        .unOrdered()
+        .baselineColumns("coal")
+        .baselineValuesForSingleColumn(null, null, null, null, null)
+        .go();
+  }
 }
 
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/fn/impl/TestCastFunctions.java b/exec/java-exec/src/test/java/org/apache/drill/exec/fn/impl/TestCastFunctions.java
index 0d884b9..73b4b94 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/exec/fn/impl/TestCastFunctions.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/fn/impl/TestCastFunctions.java
@@ -21,6 +21,7 @@ import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -30,8 +31,13 @@ import org.apache.drill.categories.SqlFunctionTest;
 import org.apache.drill.categories.UnlikelyTest;
 import org.apache.drill.common.exceptions.UserRemoteException;
 import org.apache.drill.common.expression.SchemaPath;
+import org.apache.drill.common.types.TypeProtos;
+import org.apache.drill.common.types.Types;
 import org.apache.drill.exec.planner.physical.PlannerSettings;
+import org.apache.drill.exec.record.BatchSchema;
+import org.apache.drill.exec.record.MaterializedField;
 import org.apache.drill.exec.record.RecordBatchLoader;
+import org.apache.drill.exec.record.metadata.SchemaBuilder;
 import org.apache.drill.exec.rpc.user.QueryDataBatch;
 import org.apache.drill.exec.vector.IntervalYearVector;
 import org.apache.drill.test.ClusterFixture;
@@ -46,6 +52,17 @@ import org.junit.rules.ExpectedException;
 import org.apache.drill.shaded.guava.com.google.common.collect.Lists;
 import org.apache.drill.shaded.guava.com.google.common.collect.Maps;
 
+import static org.apache.drill.common.types.TypeProtos.MinorType.BIGINT;
+import static org.apache.drill.common.types.TypeProtos.MinorType.BIT;
+import static org.apache.drill.common.types.TypeProtos.MinorType.DATE;
+import static org.apache.drill.common.types.TypeProtos.MinorType.FLOAT4;
+import static org.apache.drill.common.types.TypeProtos.MinorType.FLOAT8;
+import static org.apache.drill.common.types.TypeProtos.MinorType.INT;
+import static org.apache.drill.common.types.TypeProtos.MinorType.INTERVALYEAR;
+import static org.apache.drill.common.types.TypeProtos.MinorType.TIME;
+import static org.apache.drill.common.types.TypeProtos.MinorType.TIMESTAMP;
+import static org.apache.drill.common.types.TypeProtos.MinorType.VARCHAR;
+import static org.apache.drill.common.types.TypeProtos.MinorType.VARDECIMAL;
 import static org.apache.drill.exec.ExecTest.mockUtcDateTimeZone;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.hasItem;
@@ -749,4 +766,57 @@ public class TestCastFunctions extends ClusterTest {
       run("drop table if exists dfs.tmp.test_time_filter");
     }
   }
+
+  @Test
+  public void testCastUntypedNull() throws Exception {
+    String query = "select cast(coalesce(unk1, unk2) as %s) as coal from cp.`tpch/nation.parquet` limit 1";
+
+    Map<String, TypeProtos.MajorType> typesMap = createCastTypeMap();
+    for (Map.Entry<String, TypeProtos.MajorType> entry : typesMap.entrySet()) {
+      String q = String.format(query, entry.getKey());
+
+      MaterializedField field = MaterializedField.create("coal", entry.getValue());
+      BatchSchema expectedSchema = new SchemaBuilder()
+          .add(field)
+          .build();
+
+      // Validate schema
+      testBuilder()
+          .sqlQuery(q)
+          .schemaBaseLine(expectedSchema)
+          .go();
+
+      // Validate result
+      testBuilder()
+          .sqlQuery(q)
+          .unOrdered()
+          .baselineColumns("coal")
+          .baselineValues(new Object[] {null})
+          .go();
+    }
+  }
+
+  private static Map<String, TypeProtos.MajorType> createCastTypeMap() {
+    TypeProtos.DataMode mode = TypeProtos.DataMode.OPTIONAL;
+    Map<String, TypeProtos.MajorType> typesMap = new HashMap<>();
+    typesMap.put("BOOLEAN", Types.withMode(BIT, mode));
+    typesMap.put("INT", Types.withMode(INT, mode));
+    typesMap.put("BIGINT", Types.withMode(BIGINT, mode));
+    typesMap.put("FLOAT", Types.withMode(FLOAT4, mode));
+    typesMap.put("DOUBLE", Types.withMode(FLOAT8, mode));
+    typesMap.put("DATE", Types.withMode(DATE, mode));
+    typesMap.put("TIME", Types.withMode(TIME, mode));
+    typesMap.put("TIMESTAMP", Types.withMode(TIMESTAMP, mode));
+    typesMap.put("INTERVAL MONTH", Types.withMode(INTERVALYEAR, mode));
+    typesMap.put("INTERVAL YEAR", Types.withMode(INTERVALYEAR, mode));
+    // todo: uncomment after DRILL-6993 is resolved
+    // typesMap.put("VARBINARY(31)", Types.withPrecision(VARBINARY, mode, 31));
+    typesMap.put("VARCHAR(26)", Types.withPrecision(VARCHAR, mode, 26));
+    typesMap.put("DECIMAL(9, 2)", Types.withScaleAndPrecision(VARDECIMAL, mode, 2, 9));
+    typesMap.put("DECIMAL(18, 5)", Types.withScaleAndPrecision(VARDECIMAL, mode, 5, 18));
+    typesMap.put("DECIMAL(28, 3)", Types.withScaleAndPrecision(VARDECIMAL, mode, 3, 28));
+    typesMap.put("DECIMAL(38, 2)", Types.withScaleAndPrecision(VARDECIMAL, mode, 2, 38));
+
+    return typesMap;
+  }
 }
diff --git a/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillCursor.java b/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillCursor.java
index 7025f18..c8b8c5e 100644
--- a/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillCursor.java
+++ b/exec/jdbc/src/main/java/org/apache/drill/jdbc/impl/DrillCursor.java
@@ -470,12 +470,11 @@ public class DrillCursor implements Cursor {
         QueryDataBatch qrb = resultsListener.getNext();
 
         // (Apparently:)  Skip any spurious empty batches (batches that have
-        // zero rows and/or null data, other than the first batch (which carries
+        // zero rows and null data, other than the first batch (which carries
         // the (initial) schema but no rows)).
-        if ( afterFirstBatch ) {
-          while ( qrb != null
-                  && ( qrb.getHeader().getRowCount() == 0
-                      || qrb.getData() == null ) ) {
+        if (afterFirstBatch) {
+          while (qrb != null
+              && (qrb.getHeader().getRowCount() == 0 && qrb.getData() == null)) {
             // Empty message--dispose of and try to get another.
             logger.warn( "Spurious batch read: {}", qrb );
 
diff --git a/exec/vector/src/main/codegen/templates/AbstractFieldReader.java b/exec/vector/src/main/codegen/templates/AbstractFieldReader.java
index 4a763c2..7400d39 100644
--- a/exec/vector/src/main/codegen/templates/AbstractFieldReader.java
+++ b/exec/vector/src/main/codegen/templates/AbstractFieldReader.java
@@ -29,9 +29,9 @@ package org.apache.drill.exec.vector.complex.impl;
  * This class is generated using freemarker and the ${.template_name} template.
  */
 @SuppressWarnings("unused")
-abstract class AbstractFieldReader extends AbstractBaseReader implements FieldReader {
+public abstract class AbstractFieldReader extends AbstractBaseReader implements FieldReader {
 
-  AbstractFieldReader() {
+  public AbstractFieldReader() {
   }
 
   /**
diff --git a/exec/vector/src/main/codegen/templates/BasicTypeHelper.java b/exec/vector/src/main/codegen/templates/BasicTypeHelper.java
index 383e195..f1de685 100644
--- a/exec/vector/src/main/codegen/templates/BasicTypeHelper.java
+++ b/exec/vector/src/main/codegen/templates/BasicTypeHelper.java
@@ -64,6 +64,8 @@ public class BasicTypeHelper {
       case FIXEDCHAR: return major.getPrecision();
       case FIXED16CHAR: return major.getPrecision();
       case FIXEDBINARY: return major.getPrecision();
+      case NULL:
+        return 0;
     }
     throw new UnsupportedOperationException(buildErrorMessage("get size", major));
   }
diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/complex/impl/UntypedHolderReaderImpl.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/UntypedHolderReaderImpl.java
similarity index 92%
rename from exec/vector/src/main/java/org/apache/drill/exec/vector/complex/impl/UntypedHolderReaderImpl.java
rename to exec/vector/src/main/java/org/apache/drill/exec/vector/UntypedHolderReaderImpl.java
index 3e10d78..66f03f1 100644
--- a/exec/vector/src/main/java/org/apache/drill/exec/vector/complex/impl/UntypedHolderReaderImpl.java
+++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/UntypedHolderReaderImpl.java
@@ -15,10 +15,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.drill.exec.vector.complex.impl;
+package org.apache.drill.exec.vector;
 
 import org.apache.drill.common.types.TypeProtos;
-import org.apache.drill.exec.vector.UntypedNullHolder;
+import org.apache.drill.exec.vector.complex.impl.AbstractFieldReader;
 
 public class UntypedHolderReaderImpl extends AbstractFieldReader {
 
@@ -47,5 +47,4 @@ public class UntypedHolderReaderImpl extends AbstractFieldReader {
   public boolean isSet() {
     return false;
   }
-
 }
diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/UntypedNullVector.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/UntypedNullVector.java
index 9dd8480..b83d872 100644
--- a/exec/vector/src/main/java/org/apache/drill/exec/vector/UntypedNullVector.java
+++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/UntypedNullVector.java
@@ -17,8 +17,6 @@
  */
 package org.apache.drill.exec.vector;
 
-
-import org.apache.drill.exec.vector.complex.impl.UntypedReaderImpl;
 import org.apache.drill.shaded.guava.com.google.common.base.Preconditions;
 import io.netty.buffer.DrillBuf;
 import org.apache.drill.exec.memory.BufferAllocator;
@@ -47,7 +45,6 @@ public final class UntypedNullVector extends BaseDataValueVector implements Fixe
 
   public UntypedNullVector(MaterializedField field, BufferAllocator allocator) {
     super(field, allocator);
-    valueCount = 0;
   }
 
   @Override
@@ -77,7 +74,9 @@ public final class UntypedNullVector extends BaseDataValueVector implements Fixe
   public boolean allocateNewSafe() { return true; }
 
   @Override
-  public void allocateNew(final int valueCount) { }
+  public void allocateNew(final int valueCount) {
+    this.valueCount = valueCount;
+  }
 
   @Override
   public void reset() { }
@@ -125,7 +124,10 @@ public final class UntypedNullVector extends BaseDataValueVector implements Fixe
     return new TransferImpl((UntypedNullVector) to);
   }
 
-  public void transferTo(UntypedNullVector target) { }
+  public void transferTo(UntypedNullVector target) {
+    target.valueCount = valueCount;
+    clear();
+  }
 
   public void splitAndTransferTo(int startIndex, int length, UntypedNullVector target) { }
 
@@ -170,7 +172,11 @@ public final class UntypedNullVector extends BaseDataValueVector implements Fixe
 
   @Override
   public void copyEntry(int toIndex, ValueVector from, int fromIndex) {
-    ((UntypedNullVector) from).data.getBytes(fromIndex * 4, data, toIndex * 4, 4);
+  }
+
+  @Override
+  public void clear() {
+    valueCount = 0;
   }
 
   public final class Accessor extends BaseAccessor {
diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/complex/impl/UntypedReader.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/UntypedReader.java
similarity index 90%
rename from exec/vector/src/main/java/org/apache/drill/exec/vector/complex/impl/UntypedReader.java
rename to exec/vector/src/main/java/org/apache/drill/exec/vector/UntypedReader.java
index f15b852..ec24f2c 100644
--- a/exec/vector/src/main/java/org/apache/drill/exec/vector/complex/impl/UntypedReader.java
+++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/UntypedReader.java
@@ -15,9 +15,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.drill.exec.vector.complex.impl;
+package org.apache.drill.exec.vector;
 
-import org.apache.drill.exec.vector.UntypedNullHolder;
 import org.apache.drill.exec.vector.complex.reader.BaseReader;
 
 public interface UntypedReader extends BaseReader {
@@ -26,5 +25,4 @@ public interface UntypedReader extends BaseReader {
   int size();
   void read(UntypedNullHolder holder);
   void read(int arrayIndex, UntypedNullHolder holder);
-
 }
diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/complex/impl/UntypedReaderImpl.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/UntypedReaderImpl.java
similarity index 92%
rename from exec/vector/src/main/java/org/apache/drill/exec/vector/complex/impl/UntypedReaderImpl.java
rename to exec/vector/src/main/java/org/apache/drill/exec/vector/UntypedReaderImpl.java
index da6f63e..924156e 100644
--- a/exec/vector/src/main/java/org/apache/drill/exec/vector/complex/impl/UntypedReaderImpl.java
+++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/UntypedReaderImpl.java
@@ -15,10 +15,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.drill.exec.vector.complex.impl;
+package org.apache.drill.exec.vector;
 
 import org.apache.drill.common.types.TypeProtos;
-import org.apache.drill.exec.vector.UntypedNullHolder;
+import org.apache.drill.exec.vector.complex.impl.AbstractFieldReader;
 
 public class UntypedReaderImpl extends AbstractFieldReader {
 
@@ -46,5 +46,4 @@ public class UntypedReaderImpl extends AbstractFieldReader {
   public void read(int arrayIndex, UntypedNullHolder holder) {
     holder.isSet = 0;
   }
-
 }
diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/complex/reader/FieldReader.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/complex/reader/FieldReader.java
index 488306e..de165d0 100644
--- a/exec/vector/src/main/java/org/apache/drill/exec/vector/complex/reader/FieldReader.java
+++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/complex/reader/FieldReader.java
@@ -17,14 +17,12 @@
  */
 package org.apache.drill.exec.vector.complex.reader;
 
-import org.apache.drill.exec.vector.complex.impl.UntypedReader;
+import org.apache.drill.exec.vector.UntypedReader;
 import org.apache.drill.exec.vector.complex.reader.BaseReader.ListReader;
 import org.apache.drill.exec.vector.complex.reader.BaseReader.MapReader;
 import org.apache.drill.exec.vector.complex.reader.BaseReader.RepeatedListReader;
 import org.apache.drill.exec.vector.complex.reader.BaseReader.RepeatedMapReader;
 import org.apache.drill.exec.vector.complex.reader.BaseReader.ScalarReader;
 
-
-
 public interface FieldReader extends MapReader, ListReader, ScalarReader, RepeatedMapReader, RepeatedListReader, UntypedReader {
-}
\ No newline at end of file
+}