All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.questdb.griffin.engine.table.BwdTableReaderPageFrameCursor Maven / Gradle / Ivy

/*******************************************************************************
 *     ___                  _   ____  ____
 *    / _ \ _   _  ___  ___| |_|  _ \| __ )
 *   | | | | | | |/ _ \/ __| __| | | |  _ \
 *   | |_| | |_| |  __/\__ \ |_| |_| | |_) |
 *    \__\_\\__,_|\___||___/\__|____/|____/
 *
 *  Copyright (c) 2014-2019 Appsicle
 *  Copyright (c) 2019-2023 QuestDB
 *
 *  Licensed 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 io.questdb.griffin.engine.table;

import io.questdb.cairo.BitmapIndexReader;
import io.questdb.cairo.TableReader;
import io.questdb.cairo.sql.*;
import io.questdb.cairo.vm.NullMemoryMR;
import io.questdb.cairo.vm.api.MemoryR;
import io.questdb.std.*;
import org.jetbrains.annotations.Nullable;

public class BwdTableReaderPageFrameCursor implements PageFrameCursor {
    private final int columnCount;
    private final IntList columnIndexes;
    private final LongList columnPageAddress = new LongList();
    private final LongList columnPageNextAddress = new LongList();
    private final IntList columnSizes;
    private final TableReaderPageFrame frame = new TableReaderPageFrame();
    private final int pageFrameMaxRows;
    private final int pageFrameMinRows;
    private final LongList pageRowsRemaining = new LongList();
    private final LongList pageSizes = new LongList();
    private final IntList pages = new IntList();
    private final LongList topsRemaining = new LongList();
    private final int workerCount;
    private long currentPageFrameRowLimit;
    private DataFrameCursor dataFrameCursor;
    private TableReader reader;
    private boolean reenterDataFrame = false;
    private long reenterPartitionHi;
    private int reenterPartitionIndex;
    private long reenterPartitionLo;

    public BwdTableReaderPageFrameCursor(
            IntList columnIndexes,
            IntList columnSizes,
            int workerCount,
            int pageFrameMinRows,
            int pageFrameMaxRows
    ) {
        this.columnIndexes = columnIndexes;
        this.columnSizes = columnSizes;
        this.columnCount = columnIndexes.size();
        this.workerCount = workerCount;
        this.pageFrameMinRows = pageFrameMinRows;
        this.pageFrameMaxRows = pageFrameMaxRows;
    }

    @Override
    public void close() {
        dataFrameCursor = Misc.free(dataFrameCursor);
    }

    @Override
    public StaticSymbolTable getSymbolTable(int columnIndex) {
        return reader.getSymbolTable(columnIndexes.getQuick(columnIndex));
    }

    @Override
    public long getUpdateRowId(long rowIndex) {
        return Rows.toRowID(frame.getPartitionIndex(), frame.getPartitionLo() + rowIndex);
    }

    @Override
    public SymbolTable newSymbolTable(int columnIndex) {
        return reader.newSymbolTable(columnIndexes.getQuick(columnIndex));
    }

    @Override
    public @Nullable PageFrame next() {
        if (reenterDataFrame) {
            return computeFrame(reenterPartitionLo, reenterPartitionHi);
        }
        DataFrame dataFrame = dataFrameCursor.next();
        if (dataFrame != null) {
            reenterPartitionIndex = dataFrame.getPartitionIndex();
            final long lo = dataFrame.getRowLo();
            final long hi = dataFrame.getRowHi();
            currentPageFrameRowLimit = Math.min(
                    pageFrameMaxRows,
                    Math.max(pageFrameMinRows, (hi - lo) / workerCount)
            );
            return computeFrame(lo, hi);
        }
        return null;
    }

    public BwdTableReaderPageFrameCursor of(DataFrameCursor dataFrameCursor) {
        this.dataFrameCursor = dataFrameCursor;
        reader = dataFrameCursor.getTableReader();
        toTop();
        return this;
    }

    @Override
    public long size() {
        return reader.size();
    }

    @Override
    public void toTop() {
        dataFrameCursor.toTop();
        pages.setAll(columnCount, 0);
        topsRemaining.setAll(columnCount, 0);
        columnPageAddress.setAll(columnCount * 2, 0);
        columnPageNextAddress.setAll(columnCount * 2, 0);
        pageRowsRemaining.setAll(columnCount, -1L);
        pageSizes.setAll(columnCount * 2, -1L);
        reenterDataFrame = false;
    }

    private TableReaderPageFrame computeFrame(final long partitionLo, final long partitionHi) {
        final int base = reader.getColumnBase(reenterPartitionIndex);

        // we may need to split this data frame either along "top" lines, or along
        // max page frame sizes; to do this, we calculate min top value from given position
        long adjustedLo = Math.max(partitionLo, partitionHi - currentPageFrameRowLimit);
        for (int i = 0; i < columnCount; i++) {
            final int columnIndex = columnIndexes.getQuick(i);
            long top = reader.getColumnTop(base, columnIndex);
            if (top > adjustedLo && top < partitionHi) {
                adjustedLo = top;
            }
        }

        for (int i = 0; i < columnCount; i++) {
            final int columnIndex = columnIndexes.getQuick(i);
            final int readerColIndex = TableReader.getPrimaryColumnIndex(base, columnIndex);
            final MemoryR col = reader.getColumn(readerColIndex);
            // when the entire column is NULL we make it skip the whole of the data frame
            final long top = col instanceof NullMemoryMR ? partitionHi : reader.getColumnTop(base, columnIndex);
            final long partitionLoAdjusted = adjustedLo - top;
            final long partitionHiAdjusted = partitionHi - top;
            final int sh = columnSizes.getQuick(i);

            if (partitionHiAdjusted > 0) {
                if (sh > -1) {
                    // this assumes reader uses single page to map the whole column
                    // non-negative sh means fixed length column
                    long address = col.getPageAddress(0);
                    long addressSize = partitionHiAdjusted << sh;
                    long offset = partitionLoAdjusted << sh;
                    columnPageAddress.setQuick(i * 2, address + offset);
                    pageSizes.setQuick(i * 2, addressSize - offset);
                } else {
                    final MemoryR fixCol = reader.getColumn(readerColIndex + 1);
                    long fixAddress = fixCol.getPageAddress(0);
                    long fixAddressSize = partitionHiAdjusted << 3;
                    long fixOffset = partitionLoAdjusted << 3;

                    long varAddress = col.getPageAddress(0);
                    long varAddressSize = Unsafe.getUnsafe().getLong(fixAddress + fixAddressSize);

                    columnPageAddress.setQuick(i * 2, varAddress);
                    columnPageAddress.setQuick(i * 2 + 1, fixAddress + fixOffset);
                    pageSizes.setQuick(i * 2, varAddressSize);
                    pageSizes.setQuick(i * 2 + 1, fixAddressSize - fixOffset);
                }
            } else {
                columnPageAddress.setQuick(i * 2, 0);
                columnPageAddress.setQuick(i * 2 + 1, 0);
                pageSizes.setQuick(i * 2, (partitionHiAdjusted - partitionLoAdjusted) << (sh > -1 ? sh : 3));
                pageSizes.setQuick(i * 2 + 1, 0);
            }
        }

        // it is possible that all columns in data frame are empty, but it doesn't mean
        // the data frame size is 0; sometimes we may want to imply nulls
        if (partitionLo < adjustedLo) {
            this.reenterPartitionLo = partitionLo;
            this.reenterPartitionHi = adjustedLo;
            this.reenterDataFrame = true;
        } else {
            this.reenterDataFrame = false;
        }

        frame.partitionLo = adjustedLo;
        frame.partitionHi = partitionHi;
        frame.partitionIndex = reenterPartitionIndex;
        return frame;
    }

    private class TableReaderPageFrame implements PageFrame {
        private long partitionHi;
        private int partitionIndex;
        private long partitionLo;

        @Override
        public BitmapIndexReader getBitmapIndexReader(int columnIndex, int direction) {
            return reader.getBitmapIndexReader(partitionIndex, columnIndexes.getQuick(columnIndex), direction);
        }

        @Override
        public int getColumnShiftBits(int columnIndex) {
            return columnSizes.getQuick(columnIndex);
        }

        @Override
        public long getIndexPageAddress(int columnIndex) {
            return columnPageAddress.getQuick(columnIndex * 2 + 1);
        }

        @Override
        public long getPageAddress(int columnIndex) {
            return columnPageAddress.getQuick(columnIndex * 2);
        }

        @Override
        public long getPageSize(int columnIndex) {
            return pageSizes.getQuick(columnIndex * 2);
        }

        @Override
        public long getPartitionHi() {
            return partitionHi;
        }

        @Override
        public int getPartitionIndex() {
            return partitionIndex;
        }

        @Override
        public long getPartitionLo() {
            return partitionLo;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy