github-advanced-security[bot] commented on code in PR #15917: URL: https://github.com/apache/druid/pull/15917#discussion_r1492003925
########## processing/src/main/java/org/apache/druid/frame/write/columnar/NumericArrayFrameColumnWriter.java: ########## @@ -0,0 +1,193 @@ +/* + * 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.write.columnar; + +import org.apache.datasketches.memory.WritableMemory; +import org.apache.druid.error.DruidException; +import org.apache.druid.frame.allocation.AppendableMemory; +import org.apache.druid.frame.allocation.MemoryAllocator; +import org.apache.druid.frame.allocation.MemoryRange; +import org.apache.druid.frame.write.FrameWriterUtils; +import org.apache.druid.segment.ColumnValueSelector; + +import java.util.List; + +public abstract class NumericArrayFrameColumnWriter implements FrameColumnWriter +{ + /** + * Equivalent to {@link AppendableMemory#DEFAULT_INITIAL_ALLOCATION_SIZE} / 3, since the memory would be further split + * up into three regions + */ + private static final int INITIAL_ALLOCATION_SIZE = 120; + + public static final byte NULL_ELEMENT_MARKER = 0x00; + public static final byte NON_NULL_ELEMENT_MARKER = 0x01; + + /** + * A byte required at the beginning for type code + */ + public static final long DATA_OFFSET = 1; + + final ColumnValueSelector selector; + final MemoryAllocator allocator; + final byte typeCode; + + /** + * Row lengths: one int per row with the number of values contained by that row and all previous rows. + * Only written for multi-value and array columns. When the corresponding row is null itself, the length is + * written as -(actual length) - 1. (Guaranteed to be a negative number even if "actual length" is zero.) + */ + private final AppendableMemory cumulativeRowLengths; + + /** + * Denotes if the element of the row is null or not + */ + private final AppendableMemory rowNullityData; + + /** + * Row data. + */ + private final AppendableMemory rowData; + + private int lastCumulativeRowLength = 0; + private int lastRowLength = -1; + + + public NumericArrayFrameColumnWriter( + final ColumnValueSelector selector, + final MemoryAllocator allocator, + final byte typeCode + ) + { + this.selector = selector; + this.allocator = allocator; + this.typeCode = typeCode; + this.cumulativeRowLengths = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + this.rowNullityData = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + this.rowData = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + } + + abstract int elementSizeBytes(); + + abstract void putNull(WritableMemory memory, long offset); + + abstract void putArrayElement(WritableMemory memory, long offset, Number element); + + @Override + public boolean addSelection() + { + List<? extends Number> numericArray = FrameWriterUtils.getNumericArrayFromObject(selector.getObject()); + int rowLength = numericArray == null ? 0 : numericArray.size(); + + if ((long) lastCumulativeRowLength + rowLength > Integer.MAX_VALUE) { + return false; + } + + if (!cumulativeRowLengths.reserveAdditional(Integer.BYTES)) { + return false; + } + + if (!rowNullityData.reserveAdditional(rowLength * Byte.BYTES)) { + return false; + } + + if (!rowData.reserveAdditional(rowLength * elementSizeBytes())) { + return false; + } + + final MemoryRange<WritableMemory> rowLengthsCursor = cumulativeRowLengths.cursor(); + + if (numericArray == null) { + rowLengthsCursor.memory().putInt(rowLengthsCursor.start(), -(lastCumulativeRowLength + rowLength) - 1); + } else { + rowLengthsCursor.memory().putInt(rowLengthsCursor.start(), lastCumulativeRowLength + rowLength); + } + cumulativeRowLengths.advanceCursor(Integer.BYTES); + lastRowLength = rowLength; + lastCumulativeRowLength += rowLength; + + final MemoryRange<WritableMemory> rowNullityDataCursor = rowLength > 0 ? rowNullityData.cursor() : null; + final MemoryRange<WritableMemory> rowDataCursor = rowLength > 0 ? rowData.cursor() : null; + + for (int i = 0; i < rowLength; ++i) { + final Number element = numericArray.get(i); + final long memoryOffset = rowDataCursor.start() + ((long) elementSizeBytes() * i); Review Comment: ## Dereferenced variable may be null Variable [rowDataCursor](1) may be null at this access because of [this](2) assignment. [Show more details](https://github.com/apache/druid/security/code-scanning/6580) ########## processing/src/main/java/org/apache/druid/frame/read/columnar/LongArrayFrameColumnReader.java: ########## @@ -0,0 +1,57 @@ +/* + * 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.frame.Frame; +import org.apache.druid.frame.write.columnar.FrameColumnWriters; +import org.apache.druid.segment.column.ColumnType; + +public class LongArrayFrameColumnReader extends NumericArrayFrameColumnReader +{ + public LongArrayFrameColumnReader(int columnNumber) + { + super(FrameColumnWriters.TYPE_LONG_ARRAY, ColumnType.LONG_ARRAY, columnNumber); + } + + @Override + NumericArrayFrameColumn column(Frame frame, Memory memory, ColumnType columnType) + { + return new LongArrayFrameColumn(frame, memory, columnType); + } + + private static class LongArrayFrameColumn extends NumericArrayFrameColumn + { + public LongArrayFrameColumn( + Frame frame, + Memory memory, + ColumnType columnType + ) + { + super(frame, memory, columnType); + } + + @Override + Number getElement(Memory memory, long rowDataOffset, int cumulativeIndex) + { + return memory.getLong(rowDataOffset + (long) cumulativeIndex * Long.BYTES); Review Comment: ## User-controlled data in arithmetic expression This arithmetic expression depends on a [user-provided value](1), potentially causing an overflow. This arithmetic expression depends on a [user-provided value](2), potentially causing an overflow. [Show more details](https://github.com/apache/druid/security/code-scanning/6586) ########## processing/src/main/java/org/apache/druid/frame/read/columnar/DoubleArrayFrameColumnReader.java: ########## @@ -0,0 +1,57 @@ +/* + * 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.frame.Frame; +import org.apache.druid.frame.write.columnar.FrameColumnWriters; +import org.apache.druid.segment.column.ColumnType; + +public class DoubleArrayFrameColumnReader extends NumericArrayFrameColumnReader +{ + public DoubleArrayFrameColumnReader(int columnNumber) + { + super(FrameColumnWriters.TYPE_DOUBLE_ARRAY, ColumnType.DOUBLE_ARRAY, columnNumber); + } + + @Override + NumericArrayFrameColumn column(Frame frame, Memory memory, ColumnType columnType) + { + return new DoubleArrayFrameColumn(frame, memory, columnType); + } + + private static class DoubleArrayFrameColumn extends NumericArrayFrameColumn + { + public DoubleArrayFrameColumn( + Frame frame, + Memory memory, + ColumnType columnType + ) + { + super(frame, memory, columnType); + } + + @Override + Number getElement(Memory memory, long rowDataOffset, int cumulativeIndex) + { + return memory.getDouble(rowDataOffset + (long) cumulativeIndex * Double.BYTES); Review Comment: ## User-controlled data in arithmetic expression This arithmetic expression depends on a [user-provided value](1), potentially causing an overflow. This arithmetic expression depends on a [user-provided value](2), potentially causing an overflow. [Show more details](https://github.com/apache/druid/security/code-scanning/6584) ########## processing/src/main/java/org/apache/druid/frame/write/columnar/NumericArrayFrameColumnWriter.java: ########## @@ -0,0 +1,193 @@ +/* + * 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.write.columnar; + +import org.apache.datasketches.memory.WritableMemory; +import org.apache.druid.error.DruidException; +import org.apache.druid.frame.allocation.AppendableMemory; +import org.apache.druid.frame.allocation.MemoryAllocator; +import org.apache.druid.frame.allocation.MemoryRange; +import org.apache.druid.frame.write.FrameWriterUtils; +import org.apache.druid.segment.ColumnValueSelector; + +import java.util.List; + +public abstract class NumericArrayFrameColumnWriter implements FrameColumnWriter +{ + /** + * Equivalent to {@link AppendableMemory#DEFAULT_INITIAL_ALLOCATION_SIZE} / 3, since the memory would be further split + * up into three regions + */ + private static final int INITIAL_ALLOCATION_SIZE = 120; + + public static final byte NULL_ELEMENT_MARKER = 0x00; + public static final byte NON_NULL_ELEMENT_MARKER = 0x01; + + /** + * A byte required at the beginning for type code + */ + public static final long DATA_OFFSET = 1; + + final ColumnValueSelector selector; + final MemoryAllocator allocator; + final byte typeCode; + + /** + * Row lengths: one int per row with the number of values contained by that row and all previous rows. + * Only written for multi-value and array columns. When the corresponding row is null itself, the length is + * written as -(actual length) - 1. (Guaranteed to be a negative number even if "actual length" is zero.) + */ + private final AppendableMemory cumulativeRowLengths; + + /** + * Denotes if the element of the row is null or not + */ + private final AppendableMemory rowNullityData; + + /** + * Row data. + */ + private final AppendableMemory rowData; + + private int lastCumulativeRowLength = 0; + private int lastRowLength = -1; + + + public NumericArrayFrameColumnWriter( + final ColumnValueSelector selector, + final MemoryAllocator allocator, + final byte typeCode + ) + { + this.selector = selector; + this.allocator = allocator; + this.typeCode = typeCode; + this.cumulativeRowLengths = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + this.rowNullityData = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + this.rowData = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + } + + abstract int elementSizeBytes(); + + abstract void putNull(WritableMemory memory, long offset); + + abstract void putArrayElement(WritableMemory memory, long offset, Number element); + + @Override + public boolean addSelection() + { + List<? extends Number> numericArray = FrameWriterUtils.getNumericArrayFromObject(selector.getObject()); + int rowLength = numericArray == null ? 0 : numericArray.size(); + + if ((long) lastCumulativeRowLength + rowLength > Integer.MAX_VALUE) { + return false; + } + + if (!cumulativeRowLengths.reserveAdditional(Integer.BYTES)) { + return false; + } + + if (!rowNullityData.reserveAdditional(rowLength * Byte.BYTES)) { + return false; + } + + if (!rowData.reserveAdditional(rowLength * elementSizeBytes())) { + return false; + } + + final MemoryRange<WritableMemory> rowLengthsCursor = cumulativeRowLengths.cursor(); + + if (numericArray == null) { + rowLengthsCursor.memory().putInt(rowLengthsCursor.start(), -(lastCumulativeRowLength + rowLength) - 1); + } else { + rowLengthsCursor.memory().putInt(rowLengthsCursor.start(), lastCumulativeRowLength + rowLength); + } + cumulativeRowLengths.advanceCursor(Integer.BYTES); + lastRowLength = rowLength; + lastCumulativeRowLength += rowLength; + + final MemoryRange<WritableMemory> rowNullityDataCursor = rowLength > 0 ? rowNullityData.cursor() : null; + final MemoryRange<WritableMemory> rowDataCursor = rowLength > 0 ? rowData.cursor() : null; + + for (int i = 0; i < rowLength; ++i) { + final Number element = numericArray.get(i); + final long memoryOffset = rowDataCursor.start() + ((long) elementSizeBytes() * i); + if (element == null) { + rowNullityDataCursor.memory().putByte(rowNullityDataCursor.start() + Byte.BYTES * i, NULL_ELEMENT_MARKER); Review Comment: ## Dereferenced variable may be null Variable [rowNullityDataCursor](1) may be null at this access because of [this](2) assignment. [Show more details](https://github.com/apache/druid/security/code-scanning/6581) ########## processing/src/main/java/org/apache/druid/frame/read/columnar/NumericArrayFrameColumnReader.java: ########## @@ -0,0 +1,313 @@ +/* + * 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 it.unimi.dsi.fastutil.objects.ObjectArrays; +import org.apache.datasketches.memory.Memory; +import org.apache.druid.error.DruidException; +import org.apache.druid.frame.Frame; +import org.apache.druid.frame.write.columnar.NumericArrayFrameColumnWriter; +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.ObjectColumnAccessorBase; +import org.apache.druid.segment.ColumnValueSelector; +import org.apache.druid.segment.ObjectColumnSelector; +import org.apache.druid.segment.column.BaseColumn; +import org.apache.druid.segment.column.ColumnCapabilitiesImpl; +import org.apache.druid.segment.column.ColumnType; +import org.apache.druid.segment.data.ReadableOffset; +import org.apache.druid.segment.vector.ReadableVectorInspector; +import org.apache.druid.segment.vector.ReadableVectorOffset; +import org.apache.druid.segment.vector.VectorObjectSelector; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.Comparator; + +public abstract class NumericArrayFrameColumnReader implements FrameColumnReader +{ + private final byte typeCode; + private final ColumnType columnType; + private final int columnNumber; + + public NumericArrayFrameColumnReader(byte typeCode, ColumnType columnType, int columnNumber) + { + this.typeCode = typeCode; + this.columnType = columnType; + this.columnNumber = columnNumber; + } + + @Override + public Column readRACColumn(Frame frame) + { + final Memory memory = frame.region(columnNumber); + validate(memory); + return new ColumnAccessorBasedColumn(column(frame, memory, columnType)); + } + + @Override + public ColumnPlus readColumn(Frame frame) + { + final Memory memory = frame.region(columnNumber); + validate(memory); + return new ColumnPlus( + column(frame, memory, columnType), + ColumnCapabilitiesImpl.createSimpleArrayColumnCapabilities(columnType), + frame.numRows() + ); + } + + abstract NumericArrayFrameColumn column(Frame frame, Memory memory, ColumnType columnType); + + private void validate(final Memory region) + { + if (region.getCapacity() < NumericArrayFrameColumnWriter.DATA_OFFSET) { + throw DruidException.defensive("Column[%s] is not big enough for a header", columnNumber); + } + final byte typeCode = region.getByte(0); Review Comment: ## Possible confusion of local and field Potentially confusing name: method [validate](1) also refers to field [typeCode](2) (as this.typeCode). [Show more details](https://github.com/apache/druid/security/code-scanning/6583) ########## processing/src/main/java/org/apache/druid/frame/write/columnar/NumericArrayFrameColumnWriter.java: ########## @@ -0,0 +1,193 @@ +/* + * 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.write.columnar; + +import org.apache.datasketches.memory.WritableMemory; +import org.apache.druid.error.DruidException; +import org.apache.druid.frame.allocation.AppendableMemory; +import org.apache.druid.frame.allocation.MemoryAllocator; +import org.apache.druid.frame.allocation.MemoryRange; +import org.apache.druid.frame.write.FrameWriterUtils; +import org.apache.druid.segment.ColumnValueSelector; + +import java.util.List; + +public abstract class NumericArrayFrameColumnWriter implements FrameColumnWriter +{ + /** + * Equivalent to {@link AppendableMemory#DEFAULT_INITIAL_ALLOCATION_SIZE} / 3, since the memory would be further split + * up into three regions + */ + private static final int INITIAL_ALLOCATION_SIZE = 120; + + public static final byte NULL_ELEMENT_MARKER = 0x00; + public static final byte NON_NULL_ELEMENT_MARKER = 0x01; + + /** + * A byte required at the beginning for type code + */ + public static final long DATA_OFFSET = 1; + + final ColumnValueSelector selector; + final MemoryAllocator allocator; + final byte typeCode; + + /** + * Row lengths: one int per row with the number of values contained by that row and all previous rows. + * Only written for multi-value and array columns. When the corresponding row is null itself, the length is + * written as -(actual length) - 1. (Guaranteed to be a negative number even if "actual length" is zero.) + */ + private final AppendableMemory cumulativeRowLengths; + + /** + * Denotes if the element of the row is null or not + */ + private final AppendableMemory rowNullityData; + + /** + * Row data. + */ + private final AppendableMemory rowData; + + private int lastCumulativeRowLength = 0; + private int lastRowLength = -1; + + + public NumericArrayFrameColumnWriter( + final ColumnValueSelector selector, + final MemoryAllocator allocator, + final byte typeCode + ) + { + this.selector = selector; + this.allocator = allocator; + this.typeCode = typeCode; + this.cumulativeRowLengths = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + this.rowNullityData = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + this.rowData = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + } + + abstract int elementSizeBytes(); + + abstract void putNull(WritableMemory memory, long offset); + + abstract void putArrayElement(WritableMemory memory, long offset, Number element); + + @Override + public boolean addSelection() + { + List<? extends Number> numericArray = FrameWriterUtils.getNumericArrayFromObject(selector.getObject()); + int rowLength = numericArray == null ? 0 : numericArray.size(); + + if ((long) lastCumulativeRowLength + rowLength > Integer.MAX_VALUE) { + return false; + } + + if (!cumulativeRowLengths.reserveAdditional(Integer.BYTES)) { + return false; + } + + if (!rowNullityData.reserveAdditional(rowLength * Byte.BYTES)) { + return false; + } + + if (!rowData.reserveAdditional(rowLength * elementSizeBytes())) { + return false; + } + + final MemoryRange<WritableMemory> rowLengthsCursor = cumulativeRowLengths.cursor(); + + if (numericArray == null) { + rowLengthsCursor.memory().putInt(rowLengthsCursor.start(), -(lastCumulativeRowLength + rowLength) - 1); + } else { + rowLengthsCursor.memory().putInt(rowLengthsCursor.start(), lastCumulativeRowLength + rowLength); + } + cumulativeRowLengths.advanceCursor(Integer.BYTES); + lastRowLength = rowLength; + lastCumulativeRowLength += rowLength; + + final MemoryRange<WritableMemory> rowNullityDataCursor = rowLength > 0 ? rowNullityData.cursor() : null; + final MemoryRange<WritableMemory> rowDataCursor = rowLength > 0 ? rowData.cursor() : null; + + for (int i = 0; i < rowLength; ++i) { + final Number element = numericArray.get(i); + final long memoryOffset = rowDataCursor.start() + ((long) elementSizeBytes() * i); + if (element == null) { + rowNullityDataCursor.memory().putByte(rowNullityDataCursor.start() + Byte.BYTES * i, NULL_ELEMENT_MARKER); Review Comment: ## Result of multiplication cast to wider type Potential overflow in [int multiplication](1) before it is converted to long by use in a numeric context. [Show more details](https://github.com/apache/druid/security/code-scanning/6578) ########## processing/src/main/java/org/apache/druid/frame/read/columnar/NumericArrayFrameColumnReader.java: ########## @@ -0,0 +1,313 @@ +/* + * 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 it.unimi.dsi.fastutil.objects.ObjectArrays; +import org.apache.datasketches.memory.Memory; +import org.apache.druid.error.DruidException; +import org.apache.druid.frame.Frame; +import org.apache.druid.frame.write.columnar.NumericArrayFrameColumnWriter; +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.ObjectColumnAccessorBase; +import org.apache.druid.segment.ColumnValueSelector; +import org.apache.druid.segment.ObjectColumnSelector; +import org.apache.druid.segment.column.BaseColumn; +import org.apache.druid.segment.column.ColumnCapabilitiesImpl; +import org.apache.druid.segment.column.ColumnType; +import org.apache.druid.segment.data.ReadableOffset; +import org.apache.druid.segment.vector.ReadableVectorInspector; +import org.apache.druid.segment.vector.ReadableVectorOffset; +import org.apache.druid.segment.vector.VectorObjectSelector; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.Comparator; + +public abstract class NumericArrayFrameColumnReader implements FrameColumnReader +{ + private final byte typeCode; + private final ColumnType columnType; + private final int columnNumber; + + public NumericArrayFrameColumnReader(byte typeCode, ColumnType columnType, int columnNumber) + { + this.typeCode = typeCode; + this.columnType = columnType; + this.columnNumber = columnNumber; + } + + @Override + public Column readRACColumn(Frame frame) + { + final Memory memory = frame.region(columnNumber); + validate(memory); + return new ColumnAccessorBasedColumn(column(frame, memory, columnType)); + } + + @Override + public ColumnPlus readColumn(Frame frame) + { + final Memory memory = frame.region(columnNumber); + validate(memory); + return new ColumnPlus( + column(frame, memory, columnType), + ColumnCapabilitiesImpl.createSimpleArrayColumnCapabilities(columnType), + frame.numRows() + ); + } + + abstract NumericArrayFrameColumn column(Frame frame, Memory memory, ColumnType columnType); + + private void validate(final Memory region) + { + if (region.getCapacity() < NumericArrayFrameColumnWriter.DATA_OFFSET) { + throw DruidException.defensive("Column[%s] is not big enough for a header", columnNumber); + } + final byte typeCode = region.getByte(0); + if (typeCode != this.typeCode) { + throw DruidException.defensive( + "Column[%s] does not have the correct type code; expected[%s], got[%s]", + columnNumber, + this.typeCode, + typeCode + ); + } + } + + private static long getStartOfCumulativeLengthSection() + { + return NumericArrayFrameColumnWriter.DATA_OFFSET; + } + + private static long getStartOfRowNullityData(final int numRows) + { + return getStartOfCumulativeLengthSection() + ((long) numRows * Integer.BYTES); + } + + private static long getStartOfRowData(final Memory memory, final int numRows) + { + return getStartOfRowNullityData(numRows) + + (Byte.BYTES + * FrameColumnReaderUtils + .getAdjustedCumulativeRowLength(memory, getStartOfCumulativeLengthSection(), numRows - 1)); Review Comment: ## Result of multiplication cast to wider type Potential overflow in [int multiplication](1) before it is converted to long by use in a numeric context. [Show more details](https://github.com/apache/druid/security/code-scanning/6576) ########## processing/src/main/java/org/apache/druid/frame/write/columnar/NumericArrayFrameColumnWriter.java: ########## @@ -0,0 +1,193 @@ +/* + * 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.write.columnar; + +import org.apache.datasketches.memory.WritableMemory; +import org.apache.druid.error.DruidException; +import org.apache.druid.frame.allocation.AppendableMemory; +import org.apache.druid.frame.allocation.MemoryAllocator; +import org.apache.druid.frame.allocation.MemoryRange; +import org.apache.druid.frame.write.FrameWriterUtils; +import org.apache.druid.segment.ColumnValueSelector; + +import java.util.List; + +public abstract class NumericArrayFrameColumnWriter implements FrameColumnWriter +{ + /** + * Equivalent to {@link AppendableMemory#DEFAULT_INITIAL_ALLOCATION_SIZE} / 3, since the memory would be further split + * up into three regions + */ + private static final int INITIAL_ALLOCATION_SIZE = 120; + + public static final byte NULL_ELEMENT_MARKER = 0x00; + public static final byte NON_NULL_ELEMENT_MARKER = 0x01; + + /** + * A byte required at the beginning for type code + */ + public static final long DATA_OFFSET = 1; + + final ColumnValueSelector selector; + final MemoryAllocator allocator; + final byte typeCode; + + /** + * Row lengths: one int per row with the number of values contained by that row and all previous rows. + * Only written for multi-value and array columns. When the corresponding row is null itself, the length is + * written as -(actual length) - 1. (Guaranteed to be a negative number even if "actual length" is zero.) + */ + private final AppendableMemory cumulativeRowLengths; + + /** + * Denotes if the element of the row is null or not + */ + private final AppendableMemory rowNullityData; + + /** + * Row data. + */ + private final AppendableMemory rowData; + + private int lastCumulativeRowLength = 0; + private int lastRowLength = -1; + + + public NumericArrayFrameColumnWriter( + final ColumnValueSelector selector, + final MemoryAllocator allocator, + final byte typeCode + ) + { + this.selector = selector; + this.allocator = allocator; + this.typeCode = typeCode; + this.cumulativeRowLengths = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + this.rowNullityData = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + this.rowData = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + } + + abstract int elementSizeBytes(); + + abstract void putNull(WritableMemory memory, long offset); + + abstract void putArrayElement(WritableMemory memory, long offset, Number element); + + @Override + public boolean addSelection() + { + List<? extends Number> numericArray = FrameWriterUtils.getNumericArrayFromObject(selector.getObject()); + int rowLength = numericArray == null ? 0 : numericArray.size(); + + if ((long) lastCumulativeRowLength + rowLength > Integer.MAX_VALUE) { + return false; + } + + if (!cumulativeRowLengths.reserveAdditional(Integer.BYTES)) { + return false; + } + + if (!rowNullityData.reserveAdditional(rowLength * Byte.BYTES)) { + return false; + } + + if (!rowData.reserveAdditional(rowLength * elementSizeBytes())) { + return false; + } + + final MemoryRange<WritableMemory> rowLengthsCursor = cumulativeRowLengths.cursor(); + + if (numericArray == null) { + rowLengthsCursor.memory().putInt(rowLengthsCursor.start(), -(lastCumulativeRowLength + rowLength) - 1); + } else { + rowLengthsCursor.memory().putInt(rowLengthsCursor.start(), lastCumulativeRowLength + rowLength); + } + cumulativeRowLengths.advanceCursor(Integer.BYTES); + lastRowLength = rowLength; + lastCumulativeRowLength += rowLength; + + final MemoryRange<WritableMemory> rowNullityDataCursor = rowLength > 0 ? rowNullityData.cursor() : null; + final MemoryRange<WritableMemory> rowDataCursor = rowLength > 0 ? rowData.cursor() : null; + + for (int i = 0; i < rowLength; ++i) { + final Number element = numericArray.get(i); + final long memoryOffset = rowDataCursor.start() + ((long) elementSizeBytes() * i); + if (element == null) { + rowNullityDataCursor.memory().putByte(rowNullityDataCursor.start() + Byte.BYTES * i, NULL_ELEMENT_MARKER); + putNull(rowDataCursor.memory(), memoryOffset); + } else { + rowNullityDataCursor.memory().putByte(rowNullityDataCursor.start() + Byte.BYTES * i, NON_NULL_ELEMENT_MARKER); Review Comment: ## Result of multiplication cast to wider type Potential overflow in [int multiplication](1) before it is converted to long by use in a numeric context. [Show more details](https://github.com/apache/druid/security/code-scanning/6579) ########## processing/src/main/java/org/apache/druid/frame/read/columnar/NumericArrayFrameColumnReader.java: ########## @@ -0,0 +1,313 @@ +/* + * 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 it.unimi.dsi.fastutil.objects.ObjectArrays; +import org.apache.datasketches.memory.Memory; +import org.apache.druid.error.DruidException; +import org.apache.druid.frame.Frame; +import org.apache.druid.frame.write.columnar.NumericArrayFrameColumnWriter; +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.ObjectColumnAccessorBase; +import org.apache.druid.segment.ColumnValueSelector; +import org.apache.druid.segment.ObjectColumnSelector; +import org.apache.druid.segment.column.BaseColumn; +import org.apache.druid.segment.column.ColumnCapabilitiesImpl; +import org.apache.druid.segment.column.ColumnType; +import org.apache.druid.segment.data.ReadableOffset; +import org.apache.druid.segment.vector.ReadableVectorInspector; +import org.apache.druid.segment.vector.ReadableVectorOffset; +import org.apache.druid.segment.vector.VectorObjectSelector; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.Comparator; + +public abstract class NumericArrayFrameColumnReader implements FrameColumnReader +{ + private final byte typeCode; + private final ColumnType columnType; + private final int columnNumber; + + public NumericArrayFrameColumnReader(byte typeCode, ColumnType columnType, int columnNumber) + { + this.typeCode = typeCode; + this.columnType = columnType; + this.columnNumber = columnNumber; + } + + @Override + public Column readRACColumn(Frame frame) + { + final Memory memory = frame.region(columnNumber); + validate(memory); + return new ColumnAccessorBasedColumn(column(frame, memory, columnType)); + } + + @Override + public ColumnPlus readColumn(Frame frame) + { + final Memory memory = frame.region(columnNumber); + validate(memory); + return new ColumnPlus( + column(frame, memory, columnType), + ColumnCapabilitiesImpl.createSimpleArrayColumnCapabilities(columnType), + frame.numRows() + ); + } + + abstract NumericArrayFrameColumn column(Frame frame, Memory memory, ColumnType columnType); + + private void validate(final Memory region) + { + if (region.getCapacity() < NumericArrayFrameColumnWriter.DATA_OFFSET) { + throw DruidException.defensive("Column[%s] is not big enough for a header", columnNumber); + } + final byte typeCode = region.getByte(0); + if (typeCode != this.typeCode) { + throw DruidException.defensive( + "Column[%s] does not have the correct type code; expected[%s], got[%s]", + columnNumber, + this.typeCode, + typeCode + ); + } + } + + private static long getStartOfCumulativeLengthSection() + { + return NumericArrayFrameColumnWriter.DATA_OFFSET; + } + + private static long getStartOfRowNullityData(final int numRows) + { + return getStartOfCumulativeLengthSection() + ((long) numRows * Integer.BYTES); + } + + private static long getStartOfRowData(final Memory memory, final int numRows) + { + return getStartOfRowNullityData(numRows) + + (Byte.BYTES + * FrameColumnReaderUtils + .getAdjustedCumulativeRowLength(memory, getStartOfCumulativeLengthSection(), numRows - 1)); + } + + public abstract static class NumericArrayFrameColumn extends ObjectColumnAccessorBase implements BaseColumn + { + + private final Frame frame; + private final Memory memory; + private final ColumnType columnType; + + private final long rowNullityDataOffset; + private final long rowDataOffset; + + + public NumericArrayFrameColumn(Frame frame, Memory memory, ColumnType columnType) + { + this.frame = frame; + this.memory = memory; + this.columnType = columnType; + + this.rowNullityDataOffset = getStartOfRowNullityData(frame.numRows()); + this.rowDataOffset = getStartOfRowData(memory, frame.numRows()); + } + + @Override + public ColumnType getType() + { + return columnType; + } + + @Override + public int numRows() + { + return frame.numRows(); + } + + @Override + protected Object getVal(int rowNum) + { + return getNumericArray(physicalRow(rowNum)); + } + + @Override + protected Comparator<Object> getComparator() + { + return columnType.getNullableStrategy(); + } + + @Override + public ColumnValueSelector<?> makeColumnValueSelector(ReadableOffset offset) + { + return new ObjectColumnSelector<Object>() + { + private int cachedLogicalRow = -1; + @Nullable + private Object[] cachedValue = null; + + @Override + public void inspectRuntimeShape(RuntimeShapeInspector inspector) + { + } + + @Nullable + @Override + public Object getObject() + { + compute(); + return cachedValue; + } + + @Override + public Class<?> classOfObject() + { + return Object[].class; + } + + private void compute() + { + int currentLogicalRow = offset.getOffset(); + if (cachedLogicalRow == currentLogicalRow) { + return; + } + cachedValue = getNumericArray(physicalRow(currentLogicalRow)); + cachedLogicalRow = currentLogicalRow; + } + }; + } + + @Override + public VectorObjectSelector makeVectorObjectSelector(ReadableVectorOffset offset) + { + return new VectorObjectSelector() + { + private final Object[] vector = new Object[offset.getMaxVectorSize()]; + private int id = ReadableVectorInspector.NULL_ID; + + @Override + public Object[] getObjectVector() + { + computeVector(); + return vector; + } + + @Override + public int getMaxVectorSize() + { + return offset.getMaxVectorSize(); + } + + @Override + public int getCurrentVectorSize() + { + return offset.getCurrentVectorSize(); + } + + private void computeVector() + { + if (id == offset.getId()) { + return; + } + + if (offset.isContiguous()) { + // Contiguous offsets can have a cache optimized implementation if 'frame.isPermuted() == false', + // i.e. logicalRow == physicalRow. The implementation can separately fetch out the nullity data, and the + // element data continguously. + final int start = offset.getStartOffset(); + for (int i = 0; i < offset.getCurrentVectorSize(); ++i) { + vector[i] = getNumericArray(physicalRow(start + i)); + } + } else { + final int[] offsets = offset.getOffsets(); + for (int i = 0; i < offset.getCurrentVectorSize(); ++i) { + vector[i] = getNumericArray(physicalRow(offsets[i])); + } + + id = offset.getId(); + } + } + }; + } + + @Override + public void close() throws IOException + { + + } + + private int physicalRow(int logicalRow) + { + return frame.physicalRow(logicalRow); + } + + @Nullable + private Object[] getNumericArray(final int physicalRow) + { + final int cumulativeLength = FrameColumnReaderUtils.getCumulativeRowLength( + memory, + getStartOfCumulativeLengthSection(), + physicalRow + ); + + final int rowLength; + if (FrameColumnReaderUtils.isNullRow(cumulativeLength)) { + return null; + } else if (physicalRow == 0) { + rowLength = cumulativeLength; + } else { + final int previousCumulativeLength = FrameColumnReaderUtils.adjustCumulativeRowLength( + FrameColumnReaderUtils.getCumulativeRowLength( + memory, + getStartOfCumulativeLengthSection(), + physicalRow - 1 + ) + ); + rowLength = cumulativeLength - previousCumulativeLength; + } + + if (rowLength == 0) { + return ObjectArrays.EMPTY_ARRAY; + } + + final Object[] row = new Object[rowLength]; + for (int i = 0; i < rowLength; ++i) { + final int cumulativeIndex = cumulativeLength - rowLength + i; + row[i] = getElementNullity(cumulativeIndex) ? null : getElement(memory, rowDataOffset, cumulativeIndex); + } + + return row; + } + + private boolean getElementNullity(final int cumulativeIndex) + { + byte b = memory.getByte(rowNullityDataOffset + cumulativeIndex * Byte.BYTES); Review Comment: ## Result of multiplication cast to wider type Potential overflow in [int multiplication](1) before it is converted to long by use in a numeric context. [Show more details](https://github.com/apache/druid/security/code-scanning/6577) ########## processing/src/main/java/org/apache/druid/frame/read/columnar/FloatArrayFrameColumnReader.java: ########## @@ -0,0 +1,57 @@ +/* + * 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.frame.Frame; +import org.apache.druid.frame.write.columnar.FrameColumnWriters; +import org.apache.druid.segment.column.ColumnType; + +public class FloatArrayFrameColumnReader extends NumericArrayFrameColumnReader +{ + public FloatArrayFrameColumnReader(int columnNumber) + { + super(FrameColumnWriters.TYPE_FLOAT_ARRAY, ColumnType.FLOAT_ARRAY, columnNumber); + } + + @Override + NumericArrayFrameColumn column(Frame frame, Memory memory, ColumnType columnType) + { + return new FloatArrayFrameColumn(frame, memory, columnType); + } + + private static class FloatArrayFrameColumn extends NumericArrayFrameColumn + { + public FloatArrayFrameColumn( + Frame frame, + Memory memory, + ColumnType columnType + ) + { + super(frame, memory, columnType); + } + + @Override + Number getElement(Memory memory, long rowDataOffset, int cumulativeIndex) + { + return memory.getFloat(rowDataOffset + (long) cumulativeIndex * Float.BYTES); Review Comment: ## User-controlled data in arithmetic expression This arithmetic expression depends on a [user-provided value](1), potentially causing an overflow. This arithmetic expression depends on a [user-provided value](2), potentially causing an overflow. [Show more details](https://github.com/apache/druid/security/code-scanning/6585) ########## processing/src/main/java/org/apache/druid/frame/read/columnar/NumericArrayFrameColumnReader.java: ########## @@ -0,0 +1,313 @@ +/* + * 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 it.unimi.dsi.fastutil.objects.ObjectArrays; +import org.apache.datasketches.memory.Memory; +import org.apache.druid.error.DruidException; +import org.apache.druid.frame.Frame; +import org.apache.druid.frame.write.columnar.NumericArrayFrameColumnWriter; +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.ObjectColumnAccessorBase; +import org.apache.druid.segment.ColumnValueSelector; +import org.apache.druid.segment.ObjectColumnSelector; +import org.apache.druid.segment.column.BaseColumn; +import org.apache.druid.segment.column.ColumnCapabilitiesImpl; +import org.apache.druid.segment.column.ColumnType; +import org.apache.druid.segment.data.ReadableOffset; +import org.apache.druid.segment.vector.ReadableVectorInspector; +import org.apache.druid.segment.vector.ReadableVectorOffset; +import org.apache.druid.segment.vector.VectorObjectSelector; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.Comparator; + +public abstract class NumericArrayFrameColumnReader implements FrameColumnReader +{ + private final byte typeCode; + private final ColumnType columnType; + private final int columnNumber; + + public NumericArrayFrameColumnReader(byte typeCode, ColumnType columnType, int columnNumber) + { + this.typeCode = typeCode; + this.columnType = columnType; + this.columnNumber = columnNumber; + } + + @Override + public Column readRACColumn(Frame frame) + { + final Memory memory = frame.region(columnNumber); + validate(memory); + return new ColumnAccessorBasedColumn(column(frame, memory, columnType)); + } + + @Override + public ColumnPlus readColumn(Frame frame) + { + final Memory memory = frame.region(columnNumber); + validate(memory); + return new ColumnPlus( + column(frame, memory, columnType), + ColumnCapabilitiesImpl.createSimpleArrayColumnCapabilities(columnType), + frame.numRows() + ); + } + + abstract NumericArrayFrameColumn column(Frame frame, Memory memory, ColumnType columnType); + + private void validate(final Memory region) + { + if (region.getCapacity() < NumericArrayFrameColumnWriter.DATA_OFFSET) { + throw DruidException.defensive("Column[%s] is not big enough for a header", columnNumber); + } + final byte typeCode = region.getByte(0); + if (typeCode != this.typeCode) { + throw DruidException.defensive( + "Column[%s] does not have the correct type code; expected[%s], got[%s]", + columnNumber, + this.typeCode, + typeCode + ); + } + } + + private static long getStartOfCumulativeLengthSection() + { + return NumericArrayFrameColumnWriter.DATA_OFFSET; + } + + private static long getStartOfRowNullityData(final int numRows) + { + return getStartOfCumulativeLengthSection() + ((long) numRows * Integer.BYTES); + } + + private static long getStartOfRowData(final Memory memory, final int numRows) + { + return getStartOfRowNullityData(numRows) + + (Byte.BYTES + * FrameColumnReaderUtils + .getAdjustedCumulativeRowLength(memory, getStartOfCumulativeLengthSection(), numRows - 1)); Review Comment: ## User-controlled data in arithmetic expression This arithmetic expression depends on a [user-provided value](1), potentially causing an underflow. This arithmetic expression depends on a [user-provided value](2), potentially causing an underflow. [Show more details](https://github.com/apache/druid/security/code-scanning/6587) ########## processing/src/main/java/org/apache/druid/frame/read/columnar/NumericArrayFrameColumnReader.java: ########## @@ -0,0 +1,313 @@ +/* + * 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 it.unimi.dsi.fastutil.objects.ObjectArrays; +import org.apache.datasketches.memory.Memory; +import org.apache.druid.error.DruidException; +import org.apache.druid.frame.Frame; +import org.apache.druid.frame.write.columnar.NumericArrayFrameColumnWriter; +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.ObjectColumnAccessorBase; +import org.apache.druid.segment.ColumnValueSelector; +import org.apache.druid.segment.ObjectColumnSelector; +import org.apache.druid.segment.column.BaseColumn; +import org.apache.druid.segment.column.ColumnCapabilitiesImpl; +import org.apache.druid.segment.column.ColumnType; +import org.apache.druid.segment.data.ReadableOffset; +import org.apache.druid.segment.vector.ReadableVectorInspector; +import org.apache.druid.segment.vector.ReadableVectorOffset; +import org.apache.druid.segment.vector.VectorObjectSelector; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.Comparator; + +public abstract class NumericArrayFrameColumnReader implements FrameColumnReader +{ + private final byte typeCode; + private final ColumnType columnType; + private final int columnNumber; + + public NumericArrayFrameColumnReader(byte typeCode, ColumnType columnType, int columnNumber) + { + this.typeCode = typeCode; + this.columnType = columnType; + this.columnNumber = columnNumber; + } + + @Override + public Column readRACColumn(Frame frame) + { + final Memory memory = frame.region(columnNumber); + validate(memory); + return new ColumnAccessorBasedColumn(column(frame, memory, columnType)); + } + + @Override + public ColumnPlus readColumn(Frame frame) + { + final Memory memory = frame.region(columnNumber); + validate(memory); + return new ColumnPlus( + column(frame, memory, columnType), + ColumnCapabilitiesImpl.createSimpleArrayColumnCapabilities(columnType), + frame.numRows() + ); + } + + abstract NumericArrayFrameColumn column(Frame frame, Memory memory, ColumnType columnType); + + private void validate(final Memory region) + { + if (region.getCapacity() < NumericArrayFrameColumnWriter.DATA_OFFSET) { + throw DruidException.defensive("Column[%s] is not big enough for a header", columnNumber); + } + final byte typeCode = region.getByte(0); + if (typeCode != this.typeCode) { + throw DruidException.defensive( + "Column[%s] does not have the correct type code; expected[%s], got[%s]", + columnNumber, + this.typeCode, + typeCode + ); + } + } + + private static long getStartOfCumulativeLengthSection() + { + return NumericArrayFrameColumnWriter.DATA_OFFSET; + } + + private static long getStartOfRowNullityData(final int numRows) + { + return getStartOfCumulativeLengthSection() + ((long) numRows * Integer.BYTES); + } + + private static long getStartOfRowData(final Memory memory, final int numRows) + { + return getStartOfRowNullityData(numRows) + + (Byte.BYTES + * FrameColumnReaderUtils + .getAdjustedCumulativeRowLength(memory, getStartOfCumulativeLengthSection(), numRows - 1)); + } + + public abstract static class NumericArrayFrameColumn extends ObjectColumnAccessorBase implements BaseColumn + { + + private final Frame frame; + private final Memory memory; + private final ColumnType columnType; + + private final long rowNullityDataOffset; + private final long rowDataOffset; + + + public NumericArrayFrameColumn(Frame frame, Memory memory, ColumnType columnType) + { + this.frame = frame; + this.memory = memory; + this.columnType = columnType; + + this.rowNullityDataOffset = getStartOfRowNullityData(frame.numRows()); + this.rowDataOffset = getStartOfRowData(memory, frame.numRows()); + } + + @Override + public ColumnType getType() + { + return columnType; + } + + @Override + public int numRows() + { + return frame.numRows(); + } + + @Override + protected Object getVal(int rowNum) + { + return getNumericArray(physicalRow(rowNum)); + } + + @Override + protected Comparator<Object> getComparator() + { + return columnType.getNullableStrategy(); + } + + @Override + public ColumnValueSelector<?> makeColumnValueSelector(ReadableOffset offset) + { + return new ObjectColumnSelector<Object>() + { + private int cachedLogicalRow = -1; + @Nullable + private Object[] cachedValue = null; + + @Override + public void inspectRuntimeShape(RuntimeShapeInspector inspector) + { + } + + @Nullable + @Override + public Object getObject() + { + compute(); + return cachedValue; + } + + @Override + public Class<?> classOfObject() + { + return Object[].class; + } + + private void compute() + { + int currentLogicalRow = offset.getOffset(); + if (cachedLogicalRow == currentLogicalRow) { + return; + } + cachedValue = getNumericArray(physicalRow(currentLogicalRow)); + cachedLogicalRow = currentLogicalRow; + } + }; + } + + @Override + public VectorObjectSelector makeVectorObjectSelector(ReadableVectorOffset offset) + { + return new VectorObjectSelector() + { + private final Object[] vector = new Object[offset.getMaxVectorSize()]; + private int id = ReadableVectorInspector.NULL_ID; + + @Override + public Object[] getObjectVector() + { + computeVector(); + return vector; + } + + @Override + public int getMaxVectorSize() + { + return offset.getMaxVectorSize(); + } + + @Override + public int getCurrentVectorSize() + { + return offset.getCurrentVectorSize(); + } + + private void computeVector() + { + if (id == offset.getId()) { + return; + } + + if (offset.isContiguous()) { + // Contiguous offsets can have a cache optimized implementation if 'frame.isPermuted() == false', + // i.e. logicalRow == physicalRow. The implementation can separately fetch out the nullity data, and the + // element data continguously. + final int start = offset.getStartOffset(); + for (int i = 0; i < offset.getCurrentVectorSize(); ++i) { + vector[i] = getNumericArray(physicalRow(start + i)); + } + } else { + final int[] offsets = offset.getOffsets(); + for (int i = 0; i < offset.getCurrentVectorSize(); ++i) { + vector[i] = getNumericArray(physicalRow(offsets[i])); + } + + id = offset.getId(); + } + } + }; + } + + @Override + public void close() throws IOException + { + + } + + private int physicalRow(int logicalRow) + { + return frame.physicalRow(logicalRow); + } + + @Nullable + private Object[] getNumericArray(final int physicalRow) + { + final int cumulativeLength = FrameColumnReaderUtils.getCumulativeRowLength( + memory, + getStartOfCumulativeLengthSection(), + physicalRow + ); + + final int rowLength; + if (FrameColumnReaderUtils.isNullRow(cumulativeLength)) { + return null; + } else if (physicalRow == 0) { + rowLength = cumulativeLength; + } else { + final int previousCumulativeLength = FrameColumnReaderUtils.adjustCumulativeRowLength( + FrameColumnReaderUtils.getCumulativeRowLength( + memory, + getStartOfCumulativeLengthSection(), + physicalRow - 1 + ) + ); + rowLength = cumulativeLength - previousCumulativeLength; + } + + if (rowLength == 0) { + return ObjectArrays.EMPTY_ARRAY; + } + + final Object[] row = new Object[rowLength]; + for (int i = 0; i < rowLength; ++i) { + final int cumulativeIndex = cumulativeLength - rowLength + i; + row[i] = getElementNullity(cumulativeIndex) ? null : getElement(memory, rowDataOffset, cumulativeIndex); + } + + return row; + } + + private boolean getElementNullity(final int cumulativeIndex) + { + byte b = memory.getByte(rowNullityDataOffset + cumulativeIndex * Byte.BYTES); Review Comment: ## User-controlled data in arithmetic expression This arithmetic expression depends on a [user-provided value](1), potentially causing an overflow. This arithmetic expression depends on a [user-provided value](2), potentially causing an overflow. [Show more details](https://github.com/apache/druid/security/code-scanning/6588) ########## processing/src/main/java/org/apache/druid/frame/write/columnar/NumericArrayFrameColumnWriter.java: ########## @@ -0,0 +1,193 @@ +/* + * 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.write.columnar; + +import org.apache.datasketches.memory.WritableMemory; +import org.apache.druid.error.DruidException; +import org.apache.druid.frame.allocation.AppendableMemory; +import org.apache.druid.frame.allocation.MemoryAllocator; +import org.apache.druid.frame.allocation.MemoryRange; +import org.apache.druid.frame.write.FrameWriterUtils; +import org.apache.druid.segment.ColumnValueSelector; + +import java.util.List; + +public abstract class NumericArrayFrameColumnWriter implements FrameColumnWriter +{ + /** + * Equivalent to {@link AppendableMemory#DEFAULT_INITIAL_ALLOCATION_SIZE} / 3, since the memory would be further split + * up into three regions + */ + private static final int INITIAL_ALLOCATION_SIZE = 120; + + public static final byte NULL_ELEMENT_MARKER = 0x00; + public static final byte NON_NULL_ELEMENT_MARKER = 0x01; + + /** + * A byte required at the beginning for type code + */ + public static final long DATA_OFFSET = 1; + + final ColumnValueSelector selector; + final MemoryAllocator allocator; + final byte typeCode; + + /** + * Row lengths: one int per row with the number of values contained by that row and all previous rows. + * Only written for multi-value and array columns. When the corresponding row is null itself, the length is + * written as -(actual length) - 1. (Guaranteed to be a negative number even if "actual length" is zero.) + */ + private final AppendableMemory cumulativeRowLengths; + + /** + * Denotes if the element of the row is null or not + */ + private final AppendableMemory rowNullityData; + + /** + * Row data. + */ + private final AppendableMemory rowData; + + private int lastCumulativeRowLength = 0; + private int lastRowLength = -1; + + + public NumericArrayFrameColumnWriter( + final ColumnValueSelector selector, + final MemoryAllocator allocator, + final byte typeCode + ) + { + this.selector = selector; + this.allocator = allocator; + this.typeCode = typeCode; + this.cumulativeRowLengths = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + this.rowNullityData = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + this.rowData = AppendableMemory.create(allocator, INITIAL_ALLOCATION_SIZE); + } + + abstract int elementSizeBytes(); + + abstract void putNull(WritableMemory memory, long offset); + + abstract void putArrayElement(WritableMemory memory, long offset, Number element); + + @Override + public boolean addSelection() + { + List<? extends Number> numericArray = FrameWriterUtils.getNumericArrayFromObject(selector.getObject()); + int rowLength = numericArray == null ? 0 : numericArray.size(); + + if ((long) lastCumulativeRowLength + rowLength > Integer.MAX_VALUE) { + return false; + } + + if (!cumulativeRowLengths.reserveAdditional(Integer.BYTES)) { + return false; + } + + if (!rowNullityData.reserveAdditional(rowLength * Byte.BYTES)) { + return false; + } + + if (!rowData.reserveAdditional(rowLength * elementSizeBytes())) { + return false; + } + + final MemoryRange<WritableMemory> rowLengthsCursor = cumulativeRowLengths.cursor(); + + if (numericArray == null) { + rowLengthsCursor.memory().putInt(rowLengthsCursor.start(), -(lastCumulativeRowLength + rowLength) - 1); + } else { + rowLengthsCursor.memory().putInt(rowLengthsCursor.start(), lastCumulativeRowLength + rowLength); + } + cumulativeRowLengths.advanceCursor(Integer.BYTES); + lastRowLength = rowLength; + lastCumulativeRowLength += rowLength; + + final MemoryRange<WritableMemory> rowNullityDataCursor = rowLength > 0 ? rowNullityData.cursor() : null; + final MemoryRange<WritableMemory> rowDataCursor = rowLength > 0 ? rowData.cursor() : null; + + for (int i = 0; i < rowLength; ++i) { + final Number element = numericArray.get(i); + final long memoryOffset = rowDataCursor.start() + ((long) elementSizeBytes() * i); + if (element == null) { + rowNullityDataCursor.memory().putByte(rowNullityDataCursor.start() + Byte.BYTES * i, NULL_ELEMENT_MARKER); + putNull(rowDataCursor.memory(), memoryOffset); + } else { + rowNullityDataCursor.memory().putByte(rowNullityDataCursor.start() + Byte.BYTES * i, NON_NULL_ELEMENT_MARKER); Review Comment: ## Dereferenced variable may be null Variable [rowNullityDataCursor](1) may be null at this access because of [this](2) assignment. [Show more details](https://github.com/apache/druid/security/code-scanning/6582) ########## processing/src/main/java/org/apache/druid/frame/read/columnar/StringFrameColumnReader.java: ########## @@ -231,7 +201,9 @@ final int totalNumValues; if (multiValue) { - totalNumValues = adjustCumulativeRowLength(getCumulativeRowLength(memory, numRows - 1)); + totalNumValues = FrameColumnReaderUtils.adjustCumulativeRowLength( + FrameColumnReaderUtils.getCumulativeRowLength(memory, getStartOfCumulativeLengthSection(), numRows - 1) Review Comment: ## User-controlled data in arithmetic expression This arithmetic expression depends on a [user-provided value](1), potentially causing an underflow. This arithmetic expression depends on a [user-provided value](2), potentially causing an underflow. [Show more details](https://github.com/apache/druid/security/code-scanning/6589) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
