org.apache.druid.frame.read.columnar.DoubleFrameColumnReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of druid-processing Show documentation
Show all versions of druid-processing Show documentation
A module that is everything required to understands Druid Segments
/*
* 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.
*/
package org.apache.druid.frame.read.columnar;
import org.apache.datasketches.memory.Memory;
import org.apache.druid.common.config.NullHandling;
import org.apache.druid.frame.Frame;
import org.apache.druid.frame.write.columnar.DoubleFrameMaker;
import org.apache.druid.frame.write.columnar.FrameColumnWriters;
import org.apache.druid.java.util.common.ISE;
import org.apache.druid.query.monomorphicprocessing.RuntimeShapeInspector;
import org.apache.druid.query.rowsandcols.column.Column;
import org.apache.druid.query.rowsandcols.column.ColumnAccessorBasedColumn;
import org.apache.druid.query.rowsandcols.column.accessor.DoubleColumnAccessorBase;
import org.apache.druid.segment.ColumnValueSelector;
import org.apache.druid.segment.DoubleColumnSelector;
import org.apache.druid.segment.column.ColumnCapabilitiesImpl;
import org.apache.druid.segment.column.NumericColumn;
import org.apache.druid.segment.data.ReadableOffset;
import org.apache.druid.segment.vector.BaseDoubleVectorValueSelector;
import org.apache.druid.segment.vector.ReadableVectorInspector;
import org.apache.druid.segment.vector.ReadableVectorOffset;
import org.apache.druid.segment.vector.VectorValueSelector;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class DoubleFrameColumnReader implements FrameColumnReader
{
private final int columnNumber;
DoubleFrameColumnReader(final int columnNumber)
{
this.columnNumber = columnNumber;
}
@Override
public Column readRACColumn(Frame frame)
{
final DoubleFrameColumn frameCol = makeDoubleFrameColumn(frame);
return new ColumnAccessorBasedColumn(frameCol);
}
@Override
public ColumnPlus readColumn(final Frame frame)
{
final DoubleFrameColumn frameCol = makeDoubleFrameColumn(frame);
return new ColumnPlus(
frameCol,
ColumnCapabilitiesImpl.createSimpleNumericColumnCapabilities(frameCol.getType())
.setHasNulls(NullHandling.sqlCompatible() && frameCol.hasNulls),
frame.numRows()
);
}
@Nonnull
private DoubleFrameColumn makeDoubleFrameColumn(Frame frame)
{
final Memory memory = frame.region(columnNumber);
validate(memory, frame.numRows());
final boolean hasNulls = getHasNulls(memory);
return new DoubleFrameColumn(frame, hasNulls, memory);
}
private void validate(final Memory region, final int numRows)
{
final long memorySize = region.getCapacity();
// Check if column is big enough for a header
if (memorySize < DoubleFrameMaker.DATA_OFFSET) {
throw new ISE("Column is not big enough for a header");
}
final byte typeCode = region.getByte(0);
if (typeCode != FrameColumnWriters.TYPE_DOUBLE) {
throw new ISE("Column does not have the correct type code");
}
final boolean hasNulls = getHasNulls(region);
final int sz = DoubleFrameMaker.valueSize(hasNulls);
// Check column length again, now that we know exactly how long it should be.
if (memorySize != DoubleFrameMaker.DATA_OFFSET + (long) sz * numRows) {
throw new ISE("Column does not have the correct length");
}
}
private static boolean getHasNulls(final Memory memory)
{
return memory.getByte(Byte.BYTES) != 0;
}
private static class DoubleFrameColumn extends DoubleColumnAccessorBase implements NumericColumn
{
private final Frame frame;
private final boolean hasNulls;
private final int sz;
private final Memory memory;
private final long memoryPosition;
private DoubleFrameColumn(
final Frame frame,
final boolean hasNulls,
final Memory memory
)
{
this.frame = frame;
this.hasNulls = hasNulls;
this.sz = DoubleFrameMaker.valueSize(hasNulls);
this.memory = memory;
this.memoryPosition = DoubleFrameMaker.DATA_OFFSET;
}
@Override
public ColumnValueSelector> makeColumnValueSelector(final ReadableOffset offset)
{
return new DoubleColumnSelector()
{
@Override
public double getDouble()
{
assert NullHandling.replaceWithDefault() || !isNull();
return DoubleFrameColumn.this.getDoublePhysical(frame.physicalRow(offset.getOffset()));
}
@Override
public boolean isNull()
{
return DoubleFrameColumn.this.isNullPhysical(frame.physicalRow(offset.getOffset()));
}
@Override
public void inspectRuntimeShape(RuntimeShapeInspector inspector)
{
// Do nothing.
}
};
}
@Override
public VectorValueSelector makeVectorValueSelector(final ReadableVectorOffset theOffset)
{
class DoubleFrameColumnVectorValueSelector extends BaseDoubleVectorValueSelector
{
private final double[] doubleVector;
private final boolean[] nullVector;
private int id = ReadableVectorInspector.NULL_ID;
private DoubleFrameColumnVectorValueSelector()
{
super(theOffset);
this.doubleVector = new double[offset.getMaxVectorSize()];
this.nullVector = hasNulls ? new boolean[offset.getMaxVectorSize()] : null;
}
@Nullable
@Override
public boolean[] getNullVector()
{
computeVectorsIfNeeded();
return nullVector;
}
@Override
public double[] getDoubleVector()
{
computeVectorsIfNeeded();
return doubleVector;
}
private void computeVectorsIfNeeded()
{
if (id == offset.getId()) {
return;
}
if (offset.isContiguous()) {
final int start = offset.getStartOffset();
for (int i = 0; i < offset.getCurrentVectorSize(); i++) {
final int physicalRow = frame.physicalRow(i + start);
doubleVector[i] = getDoublePhysical(physicalRow);
if (hasNulls) {
nullVector[i] = isNullPhysical(physicalRow);
}
}
} else {
final int[] offsets = offset.getOffsets();
for (int i = 0; i < offset.getCurrentVectorSize(); i++) {
final int physicalRow = frame.physicalRow(offsets[i]);
doubleVector[i] = getDoublePhysical(physicalRow);
if (hasNulls) {
nullVector[i] = isNullPhysical(physicalRow);
}
}
}
id = offset.getId();
}
}
return new DoubleFrameColumnVectorValueSelector();
}
@Override
public int length()
{
return frame.numRows();
}
@Override
public long getLongSingleValueRow(final int rowNum)
{
// Need bounds checking, since getDouble(physicalRow) doesn't do it.
if (rowNum < 0 || rowNum >= frame.numRows()) {
throw new ISE("Row [%d] out of bounds", rowNum);
}
return (long) getDoublePhysical(frame.physicalRow(rowNum));
}
@Override
public void close()
{
// Do nothing.
}
@Override
public void inspectRuntimeShape(final RuntimeShapeInspector inspector)
{
// Do nothing.
}
@Override
public int numRows()
{
return length();
}
@Override
public boolean isNull(int rowNum)
{
return isNullPhysical(frame.physicalRow(rowNum));
}
@Override
public double getDouble(int rowNum)
{
return getDoublePhysical(frame.physicalRow(rowNum));
}
private boolean isNullPhysical(final int physicalRow)
{
if (hasNulls) {
final long rowPosition = memoryPosition + (long) sz * physicalRow;
return memory.getByte(rowPosition) != 0;
} else {
return false;
}
}
private double getDoublePhysical(final int physicalRow)
{
final long rowPosition = memoryPosition + (long) sz * physicalRow;
if (hasNulls) {
return memory.getDouble(rowPosition + 1);
} else {
return memory.getDouble(rowPosition);
}
}
}
}