diff --git a/processing/src/main/java/org/apache/druid/segment/DimensionSelector.java b/processing/src/main/java/org/apache/druid/segment/DimensionSelector.java index f47ea8cca9db..ba5b202cbaec 100644 --- a/processing/src/main/java/org/apache/druid/segment/DimensionSelector.java +++ b/processing/src/main/java/org/apache/druid/segment/DimensionSelector.java @@ -311,6 +311,12 @@ public void inspectRuntimeShape(RuntimeShapeInspector inspector) { // nothing to inspect } + + @Override + public boolean isNull() + { + return NullHandling.sqlCompatible(); + } } } } diff --git a/processing/src/main/java/org/apache/druid/segment/serde/NullColumnPartSerde.java b/processing/src/main/java/org/apache/druid/segment/serde/NullColumnPartSerde.java index 12e47eef734c..95df716b2ce5 100644 --- a/processing/src/main/java/org/apache/druid/segment/serde/NullColumnPartSerde.java +++ b/processing/src/main/java/org/apache/druid/segment/serde/NullColumnPartSerde.java @@ -33,6 +33,8 @@ import org.apache.druid.segment.vector.NilVectorSelector; import org.apache.druid.segment.vector.ReadableVectorOffset; import org.apache.druid.segment.vector.SingleValueDimensionVectorSelector; +import org.apache.druid.segment.vector.VectorObjectSelector; +import org.apache.druid.segment.vector.VectorValueSelector; import javax.annotation.Nullable; import java.nio.channels.WritableByteChannel; @@ -204,7 +206,19 @@ public MultiValueDimensionVectorSelector makeMultiValueDimensionVectorSelector( ReadableVectorOffset vectorOffset ) { - throw new RuntimeException("This method should not be called for null-only columns"); + throw new UnsupportedOperationException("This method should not be called for null-only columns"); + } + + @Override + public VectorValueSelector makeVectorValueSelector(ReadableVectorOffset offset) + { + return NilVectorSelector.create(offset); + } + + @Override + public VectorObjectSelector makeVectorObjectSelector(ReadableVectorOffset offset) + { + return NilVectorSelector.create(offset); } @Override diff --git a/processing/src/test/java/org/apache/druid/segment/serde/NullColumnPartSerdeTest.java b/processing/src/test/java/org/apache/druid/segment/serde/NullColumnPartSerdeTest.java index d57dda488930..4329bedf1263 100644 --- a/processing/src/test/java/org/apache/druid/segment/serde/NullColumnPartSerdeTest.java +++ b/processing/src/test/java/org/apache/druid/segment/serde/NullColumnPartSerdeTest.java @@ -21,12 +21,26 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.druid.common.config.NullHandling; import org.apache.druid.jackson.DefaultObjectMapper; +import org.apache.druid.segment.ColumnValueSelector; +import org.apache.druid.segment.DimensionSelector; +import org.apache.druid.segment.SimpleAscendingOffset; +import org.apache.druid.segment.column.BaseColumn; import org.apache.druid.segment.column.ColumnBuilder; import org.apache.druid.segment.column.ColumnCapabilities; import org.apache.druid.segment.column.ColumnConfig; +import org.apache.druid.segment.column.ColumnHolder; +import org.apache.druid.segment.column.DictionaryEncodedColumn; +import org.apache.druid.segment.column.Types; import org.apache.druid.segment.column.ValueType; +import org.apache.druid.segment.data.ReadableOffset; import org.apache.druid.segment.data.RoaringBitmapSerdeFactory; +import org.apache.druid.segment.vector.NoFilterVectorOffset; +import org.apache.druid.segment.vector.ReadableVectorOffset; +import org.apache.druid.segment.vector.SingleValueDimensionVectorSelector; +import org.apache.druid.segment.vector.VectorObjectSelector; +import org.apache.druid.segment.vector.VectorValueSelector; import org.apache.druid.testing.InitializedNullHandlingTest; import org.junit.Assert; import org.junit.Test; @@ -53,6 +67,7 @@ public void testDeserializer() final ColumnBuilder builder = new ColumnBuilder().setType(ValueType.DOUBLE); partSerde.getDeserializer().read(Mockito.mock(ByteBuffer.class), builder, Mockito.mock(ColumnConfig.class)); final ColumnCapabilities columnCapabilities = builder.build().getCapabilities(); + Assert.assertTrue(Types.is(columnCapabilities, ValueType.DOUBLE)); Assert.assertTrue(columnCapabilities.hasNulls().isTrue()); Assert.assertTrue(columnCapabilities.hasMultipleValues().isFalse()); Assert.assertTrue(columnCapabilities.isFilterable()); @@ -61,4 +76,128 @@ public void testDeserializer() Assert.assertTrue(columnCapabilities.areDictionaryValuesSorted().isTrue()); Assert.assertTrue(columnCapabilities.areDictionaryValuesUnique().isTrue()); } + + @Test + public void testDimensionSelector() + { + final NullColumnPartSerde partSerde = new NullColumnPartSerde(10, new RoaringBitmapSerdeFactory(null)); + final ColumnBuilder builder = new ColumnBuilder().setType(ValueType.STRING); + partSerde.getDeserializer().read(Mockito.mock(ByteBuffer.class), builder, Mockito.mock(ColumnConfig.class)); + ColumnHolder holder = builder.build(); + + BaseColumn theColumn = holder.getColumn(); + Assert.assertTrue(theColumn instanceof DictionaryEncodedColumn); + DictionaryEncodedColumn dictionaryEncodedColumn = (DictionaryEncodedColumn) theColumn; + + ReadableOffset offset = new SimpleAscendingOffset(10); + DimensionSelector dimensionSelector = dictionaryEncodedColumn.makeDimensionSelector( + offset, + null + ); + Assert.assertNull(dimensionSelector.getObject()); + Assert.assertEquals(1, dimensionSelector.getRow().size()); + Assert.assertEquals(0, dimensionSelector.getRow().get(0)); + } + + @Test + public void testDimensionVectorSelector() + { + final NullColumnPartSerde partSerde = new NullColumnPartSerde(10, new RoaringBitmapSerdeFactory(null)); + final ColumnBuilder builder = new ColumnBuilder().setType(ValueType.STRING); + partSerde.getDeserializer().read(Mockito.mock(ByteBuffer.class), builder, Mockito.mock(ColumnConfig.class)); + ColumnHolder holder = builder.build(); + + BaseColumn theColumn = holder.getColumn(); + Assert.assertTrue(theColumn instanceof DictionaryEncodedColumn); + DictionaryEncodedColumn dictionaryEncodedColumn = (DictionaryEncodedColumn) theColumn; + + ReadableVectorOffset vectorOffset = new NoFilterVectorOffset(8, 0, 10); + + SingleValueDimensionVectorSelector vectorSelector = + dictionaryEncodedColumn.makeSingleValueDimensionVectorSelector(vectorOffset); + + int[] rowVector = vectorSelector.getRowVector(); + for (int i = 0; i < vectorOffset.getCurrentVectorSize(); i++) { + Assert.assertEquals(0, rowVector[i]); + Assert.assertNull(vectorSelector.lookupName(rowVector[i])); + } + + Assert.assertThrows(UnsupportedOperationException.class, () -> { + dictionaryEncodedColumn.makeMultiValueDimensionVectorSelector(vectorOffset); + }); + } + + @Test + public void testVectorObjectSelector() + { + final NullColumnPartSerde partSerde = new NullColumnPartSerde(10, new RoaringBitmapSerdeFactory(null)); + final ColumnBuilder builder = new ColumnBuilder().setType(ValueType.STRING); + partSerde.getDeserializer().read(Mockito.mock(ByteBuffer.class), builder, Mockito.mock(ColumnConfig.class)); + ColumnHolder holder = builder.build(); + + BaseColumn theColumn = holder.getColumn(); + + ReadableVectorOffset vectorOffset = new NoFilterVectorOffset(8, 0, 10); + + VectorObjectSelector objectSelector = theColumn.makeVectorObjectSelector(vectorOffset); + Object[] vector = objectSelector.getObjectVector(); + for (int i = 0; i < vectorOffset.getCurrentVectorSize(); i++) { + Assert.assertNull(vector[i]); + } + } + + @Test + public void testColumnValueSelector() + { + final NullColumnPartSerde partSerde = new NullColumnPartSerde(10, new RoaringBitmapSerdeFactory(null)); + final ColumnBuilder builder = new ColumnBuilder().setType(ValueType.DOUBLE); + partSerde.getDeserializer().read(Mockito.mock(ByteBuffer.class), builder, Mockito.mock(ColumnConfig.class)); + ColumnHolder holder = builder.build(); + + BaseColumn theColumn = holder.getColumn(); + + ReadableOffset offset = new SimpleAscendingOffset(10); + ColumnValueSelector valueSelector = theColumn.makeColumnValueSelector(offset); + + if (NullHandling.sqlCompatible()) { + Assert.assertTrue(valueSelector.isNull()); + } else { + Assert.assertFalse(valueSelector.isNull()); + } + Assert.assertEquals(0.0, valueSelector.getDouble(), 0.0); + } + + @Test + public void testVectorValueSelector() + { + final NullColumnPartSerde partSerde = new NullColumnPartSerde(10, new RoaringBitmapSerdeFactory(null)); + final ColumnBuilder builder = new ColumnBuilder().setType(ValueType.DOUBLE); + partSerde.getDeserializer().read(Mockito.mock(ByteBuffer.class), builder, Mockito.mock(ColumnConfig.class)); + ColumnHolder holder = builder.build(); + + BaseColumn theColumn = holder.getColumn(); + ReadableVectorOffset vectorOffset = new NoFilterVectorOffset(8, 0, 10); + + VectorValueSelector selector = theColumn.makeVectorValueSelector(vectorOffset); + double[] vector = selector.getDoubleVector(); + boolean[] nulls = selector.getNullVector(); + for (int i = 0; i < vectorOffset.getCurrentVectorSize(); i++) { + Assert.assertEquals(0.0, vector[i], 0.0); + if (NullHandling.sqlCompatible()) { + Assert.assertTrue(nulls[i]); + } else { + Assert.assertFalse(nulls[i]); + } + } + } + + @Test + public void testIndexSupplier() + { + final NullColumnPartSerde partSerde = new NullColumnPartSerde(10, new RoaringBitmapSerdeFactory(null)); + final ColumnBuilder builder = new ColumnBuilder().setType(ValueType.DOUBLE); + partSerde.getDeserializer().read(Mockito.mock(ByteBuffer.class), builder, Mockito.mock(ColumnConfig.class)); + ColumnHolder holder = builder.build(); + Assert.assertNull(holder.getIndexSupplier()); + } }