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

io.deephaven.sql.LogicalSortAdapter Maven / Gradle / Ivy

/**
 * Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending
 */
package io.deephaven.sql;

import io.deephaven.api.ColumnName;
import io.deephaven.api.SortColumn;
import io.deephaven.api.expression.Expression;
import io.deephaven.api.literal.Literal;
import io.deephaven.qst.table.SortTable;
import io.deephaven.qst.table.TableSpec;
import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.rel.RelFieldCollation.NullDirection;
import org.apache.calcite.rel.logical.LogicalSort;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rex.RexInputRef;

import java.util.Objects;

final class LogicalSortAdapter {

    public static TableSpec namedTable(LogicalSort sort, NamedAdapter namedAdapter) {
        return of(sort, namedAdapter, namedAdapter);
    }

    public static TableSpec indexTable(LogicalSort sort, IndexRef indexRef) {
        return of(sort, indexRef, indexRef);
    }

    private static TableSpec of(LogicalSort sort, RelNodeAdapter relNodeAdapter, FieldAdapter fieldAdapter) {
        // SQLTODO(sort-offset)
        if (sort.offset != null) {
            throw new UnsupportedOperationException("SQLTODO(sort-offset): QST does not support slice...");
        }
        final TableSpec parent = relNodeAdapter.table(sort.getInput());
        final TableSpec sortTable;
        if (sort.collation.getFieldCollations().isEmpty()) {
            // Calcite represents `SELECT * FROM my_table LIMIT 7` as a LogicalSort with an empty field collation.
            // We can skip all of the SortTable.builder() wrapping in this case.
            sortTable = parent;
        } else {
            final SortTable.Builder builder = SortTable.builder().parent(parent);
            for (RelFieldCollation fieldCollation : sort.collation.getFieldCollations()) {
                final int inputFieldIndex = fieldCollation.getFieldIndex();
                final RelDataTypeField inputField = sort.getInput().getRowType().getFieldList().get(inputFieldIndex);
                final RexInputRef inputRef = new RexInputRef(inputFieldIndex, inputField.getType());
                final ColumnName inputColumnName = fieldAdapter.input(inputRef, inputField);
                switch (fieldCollation.direction) {
                    case ASCENDING:
                        if (fieldCollation.nullDirection == NullDirection.LAST) {
                            throw new UnsupportedOperationException(
                                    "Deephaven does not currently support ascending sort with nulls last");
                        }
                        builder.addColumns(SortColumn.asc(inputColumnName));
                        break;
                    case DESCENDING:
                        if (fieldCollation.nullDirection == NullDirection.FIRST) {
                            throw new UnsupportedOperationException(
                                    "Deephaven does not currently support descending sort with nulls first");
                        }
                        builder.addColumns(SortColumn.desc(inputColumnName));
                        break;
                    default:
                        throw new UnsupportedOperationException(
                                "Unsupported sort direction " + fieldCollation.direction);
                }
            }
            sortTable = builder.build();
        }
        if (sort.fetch != null) {
            final Expression expression = fieldAdapter.expression(sort, sort.fetch);
            if (!(expression instanceof Literal)) {
                throw new UnsupportedOperationException("Deephaven only supports literals for FETCH");
            }
            return ((Literal) expression).walk(new ApplyHead(sortTable));
        }
        return sortTable;
    }

    private static class ApplyHead implements Literal.Visitor {
        private final TableSpec input;

        public ApplyHead(TableSpec input) {
            this.input = Objects.requireNonNull(input);
        }

        @Override
        public TableSpec visit(int literal) {
            return input.head(literal);
        }

        @Override
        public TableSpec visit(long literal) {
            return input.head(literal);
        }

        @Override
        public TableSpec visit(byte literal) {
            return input.head(literal);
        }

        @Override
        public TableSpec visit(short literal) {
            return input.head(literal);
        }

        @Override
        public TableSpec visit(boolean literal) {
            throw new UnsupportedOperationException("Unable to LIMIT number of rows from boolean literal");
        }

        @Override
        public TableSpec visit(float literal) {
            throw new UnsupportedOperationException("Unable to LIMIT number of rows from float literal");
        }

        @Override
        public TableSpec visit(double literal) {
            throw new UnsupportedOperationException("Unable to LIMIT number of rows from double literal");
        }

        @Override
        public TableSpec visit(String literal) {
            throw new UnsupportedOperationException("Unable to LIMIT number of rows from String literal");
        }

        @Override
        public TableSpec visit(char literal) {
            throw new UnsupportedOperationException("Unable to LIMIT number of rows from char literal");
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy