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

io.deephaven.engine.table.impl.MemoizedOperationKey Maven / Gradle / Ivy

There is a newer version: 0.37.1
Show newest version
/**
 * Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending
 */
package io.deephaven.engine.table.impl;

import io.deephaven.api.ColumnName;
import io.deephaven.api.JoinMatch;
import io.deephaven.api.RangeJoinMatch;
import io.deephaven.api.agg.Aggregation;
import io.deephaven.engine.table.Table;
import io.deephaven.engine.table.WouldMatchPair;
import io.deephaven.engine.table.impl.select.*;
import io.deephaven.engine.table.impl.sources.regioned.SymbolTableSource;
import org.jetbrains.annotations.NotNull;

import java.lang.ref.WeakReference;
import java.util.*;

/**
 * Indices for memoized operations on QueryTable.
 * 

* When a null key is returned from one of the static methods; the operation will not be memoized (e.g., if we might * depend on the query scope; we can't memoize the operation). */ public abstract class MemoizedOperationKey { /** * Returns true if the attributes are compatible for this operation. If two table are identical, but for attributes * many of the operations can be reused. In cases where they can, it would be wasteful to reapply them. * * @param oldAttributes the attributes on the table that already is memoized * @param newAttributes the attributes on the table this is not yet memoized * * @return true if the attributes are compatible for this operation. */ boolean attributesCompatible(Map oldAttributes, Map newAttributes) { // this is the safe default return false; } /** * Returns the Copy type for this operation. * * @return the attribute copy type that should be used when transferring a memoized table across a copy. */ BaseTable.CopyAttributeOperation copyType() { throw new UnsupportedOperationException(); } BaseTable.CopyAttributeOperation getParentCopyType() { return BaseTable.CopyAttributeOperation.None; } abstract static class AttributeAgnosticMemoizedOperationKey extends MemoizedOperationKey { @Override boolean attributesCompatible(Map oldAttributes, Map newAttributes) { return true; } @Override abstract BaseTable.CopyAttributeOperation copyType(); } public interface Provider { MemoizedOperationKey getMemoKey(); } static MemoizedOperationKey selectUpdateViewOrUpdateView(SelectColumn[] selectColumn, SelectUpdateViewOrUpdateView.Flavor flavor) { if (isMemoizable(selectColumn)) { return new SelectUpdateViewOrUpdateView(selectColumn, flavor); } else { return null; } } public static MemoizedOperationKey flatten() { return Flatten.FLATTEN_INSTANCE; } static MemoizedOperationKey sort(SortPair[] sortPairs) { return new Sort(sortPairs); } static MemoizedOperationKey dropColumns(String[] columns) { return new DropColumns(columns); } static MemoizedOperationKey filter(WhereFilter[] filters) { if (Arrays.stream(filters).allMatch(WhereFilter::canMemoize)) { return new Filter(filters); } return null; } public static MemoizedOperationKey reverse() { return Reverse.REVERSE_INSTANCE; } public static MemoizedOperationKey tree(String idColumn, String parentColumn) { return new Tree(idColumn, parentColumn); } public static MemoizedOperationKey aggBy( Collection aggregations, boolean preserveEmpty, Table initialGroups, Collection groupByColumns) { return new AggBy(new ArrayList<>(aggregations), preserveEmpty, initialGroups, new ArrayList<>(groupByColumns)); } public static MemoizedOperationKey partitionBy(boolean dropKeys, Collection groupByColumns) { return new PartitionBy(dropKeys, new ArrayList<>(groupByColumns)); } public static MemoizedOperationKey rollup(Collection aggregations, Collection groupByColumns, boolean includeConstituents) { return new Rollup(new AggBy(new ArrayList<>(aggregations), false, null, new ArrayList<>(groupByColumns)), includeConstituents); } public static MemoizedOperationKey blinkToAppendOnly(final long sizeLimit, @NotNull final Object key) { return new BlinkToAppendOnly(sizeLimit, key); } private static boolean isMemoizable(SelectColumn[] selectColumn) { return Arrays.stream(selectColumn) .allMatch(sc -> sc instanceof SourceColumn || sc instanceof ReinterpretedColumn); } private static class Flatten extends AttributeAgnosticMemoizedOperationKey { static final Flatten FLATTEN_INSTANCE = new Flatten(); @Override public int hashCode() { return getClass().hashCode(); } @Override public boolean equals(Object obj) { return obj instanceof Flatten; } @Override BaseTable.CopyAttributeOperation copyType() { return BaseTable.CopyAttributeOperation.Flatten; } } static class SelectUpdateViewOrUpdateView extends AttributeAgnosticMemoizedOperationKey { public enum Flavor { Select, Update, View, UpdateView } private final SelectColumn[] selectColumns; private final Flavor flavor; private SelectUpdateViewOrUpdateView(SelectColumn[] selectColumns, Flavor flavor) { this.selectColumns = selectColumns; this.flavor = flavor; } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } final SelectUpdateViewOrUpdateView selectOrView = (SelectUpdateViewOrUpdateView) other; return flavor == selectOrView.flavor && Arrays.equals(selectColumns, selectOrView.selectColumns); } @Override public int hashCode() { return Objects.hash(flavor, Arrays.hashCode(selectColumns)); } @Override BaseTable.CopyAttributeOperation copyType() { switch (flavor) { case View: case UpdateView: return BaseTable.CopyAttributeOperation.View; case Select: case Update: // turns out select doesn't copy attributes, maybe we should more accurately codify that return BaseTable.CopyAttributeOperation.None; default: throw new UnsupportedOperationException("Unexpected flavor " + flavor); } } } static class DropColumns extends AttributeAgnosticMemoizedOperationKey { private final String[] columns; private DropColumns(String[] columns) { this.columns = Arrays.copyOf(columns, columns.length); Arrays.sort(this.columns); } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } final DropColumns dropColumns = (DropColumns) other; return Arrays.equals(columns, dropColumns.columns); } @Override public int hashCode() { return Arrays.hashCode(columns); } @Override BaseTable.CopyAttributeOperation copyType() { return BaseTable.CopyAttributeOperation.DropColumns; } } private static class Sort extends MemoizedOperationKey { private final SortPair[] sortPairs; private Sort(SortPair[] sortPairs) { this.sortPairs = sortPairs; } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } final Sort sort = (Sort) other; return Arrays.equals(sortPairs, sort.sortPairs); } @Override public int hashCode() { return Arrays.hashCode(sortPairs); } @Override boolean attributesCompatible(Map oldAttributes, Map newAttributes) { final String parentRestrictions = (String) oldAttributes.get(Table.SORTABLE_COLUMNS_ATTRIBUTE); final String newRestrictions = (String) newAttributes.get(Table.SORTABLE_COLUMNS_ATTRIBUTE); return Objects.equals(parentRestrictions, newRestrictions); } @Override BaseTable.CopyAttributeOperation copyType() { return BaseTable.CopyAttributeOperation.Sort; } } private static class Reverse extends AttributeAgnosticMemoizedOperationKey { static final Reverse REVERSE_INSTANCE = new Reverse(); @Override public boolean equals(final Object other) { return other != null && getClass() == other.getClass(); } @Override public int hashCode() { return getClass().hashCode(); } @Override BaseTable.CopyAttributeOperation copyType() { return BaseTable.CopyAttributeOperation.Reverse; } } private static class Filter extends AttributeAgnosticMemoizedOperationKey { private final WhereFilter[] filters; private Filter(WhereFilter[] filters) { this.filters = filters; } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } final Filter filter = (Filter) other; return Arrays.equals(filters, filter.filters); } @Override public int hashCode() { return Arrays.hashCode(filters); } @Override BaseTable.CopyAttributeOperation copyType() { return BaseTable.CopyAttributeOperation.Filter; } } private static class Tree extends MemoizedOperationKey { private final String idColumn; private final String parentColumn; private Tree(String idColumn, String parentColumn) { this.idColumn = idColumn; this.parentColumn = parentColumn; } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } final Tree tree = (Tree) other; return Objects.equals(idColumn, tree.idColumn) && Objects.equals(parentColumn, tree.parentColumn); } @Override public int hashCode() { return Objects.hash(idColumn, parentColumn); } @Override BaseTable.CopyAttributeOperation copyType() { return BaseTable.CopyAttributeOperation.Tree; } @Override BaseTable.CopyAttributeOperation getParentCopyType() { return BaseTable.CopyAttributeOperation.TreeCopy; } } private static class AggBy extends AttributeAgnosticMemoizedOperationKey { private final List aggregations; private final boolean preserveEmpty; private final WeakReference

initialGroups; private final List groupByColumns; private final int cachedHashCode; private AggBy( List aggregations, boolean preserveEmpty, Table initialGroups, List groupByColumns) { this.aggregations = aggregations; this.preserveEmpty = preserveEmpty; this.initialGroups = initialGroups == null ? null : new WeakReference<>(initialGroups); this.groupByColumns = groupByColumns; int hash = aggregations.hashCode(); hash = 31 * hash + Boolean.hashCode(preserveEmpty); hash = 31 * hash + System.identityHashCode(initialGroups); hash = 31 * hash + groupByColumns.hashCode(); this.cachedHashCode = hash; } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } AggBy aggBy = (AggBy) other; return aggregations.equals(aggBy.aggregations) && preserveEmpty == aggBy.preserveEmpty && equalWeakRefsByReferentIdentity(initialGroups, aggBy.initialGroups) && groupByColumns.equals(aggBy.groupByColumns); } @Override public int hashCode() { return cachedHashCode; } @Override BaseTable.CopyAttributeOperation copyType() { return BaseTable.CopyAttributeOperation.None; } } static class PartitionBy extends MemoizedOperationKey { private final boolean dropKeys; private final List groupByColumns; private PartitionBy(boolean dropKeys, List groupByColumns) { this.dropKeys = dropKeys; this.groupByColumns = groupByColumns; } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } PartitionBy that = (PartitionBy) other; return dropKeys == that.dropKeys && groupByColumns.equals(that.groupByColumns); } @Override public int hashCode() { int result = (dropKeys ? 1 : 0); result = 31 * result + groupByColumns.hashCode(); return result; } } private static class Rollup extends AttributeAgnosticMemoizedOperationKey { private final AggBy aggBy; private final boolean includeConstituents; Rollup(AggBy aggBy, boolean includeConstituents) { this.includeConstituents = includeConstituents; this.aggBy = aggBy; } @Override public int hashCode() { return 31 * aggBy.hashCode() + Boolean.hashCode(includeConstituents); } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } final Rollup rollup = (Rollup) other; return includeConstituents == rollup.includeConstituents && Objects.equals(aggBy, rollup.aggBy); } @Override BaseTable.CopyAttributeOperation copyType() { return BaseTable.CopyAttributeOperation.Rollup; } @Override BaseTable.CopyAttributeOperation getParentCopyType() { return BaseTable.CopyAttributeOperation.RollupCopy; } } public static MemoizedOperationKey symbolTable(@NotNull final SymbolTableSource symbolTableSource, final boolean useLookupCaching) { return new SymbolTable(symbolTableSource, useLookupCaching); } private static final class SymbolTable extends MemoizedOperationKey { private final SymbolTableSource symbolTableSource; private final boolean useLookupCaching; private SymbolTable(@NotNull final SymbolTableSource symbolTableSource, final boolean useLookupCaching) { this.symbolTableSource = symbolTableSource; this.useLookupCaching = useLookupCaching; } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } final SymbolTable that = (SymbolTable) other; // NB: We use the symbolTableSource's identity for comparison return symbolTableSource == that.symbolTableSource && useLookupCaching == that.useLookupCaching; } @Override public int hashCode() { return 31 * System.identityHashCode(symbolTableSource) + Boolean.hashCode(useLookupCaching); } } private static final class WouldMatch extends AttributeAgnosticMemoizedOperationKey { private final WouldMatchPair[] pairs; private WouldMatch(WouldMatchPair[] pairs) { this.pairs = pairs; } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } final WouldMatch wouldMatch = (WouldMatch) other; return Arrays.equals(pairs, wouldMatch.pairs); } @Override public int hashCode() { return Arrays.hashCode(pairs); } @Override BaseTable.CopyAttributeOperation copyType() { return BaseTable.CopyAttributeOperation.WouldMatch; } } public static MemoizedOperationKey wouldMatch(WouldMatchPair... pairs) { return new WouldMatch(pairs); } private static class CrossJoin extends AttributeAgnosticMemoizedOperationKey { private final WeakReference
rightTableReference; private final MatchPair[] columnsToMatch; private final MatchPair[] columnsToAdd; private final int numRightBitsToReserve; private final int cachedHashCode; CrossJoin(final Table rightTable, final MatchPair[] columnsToMatch, final MatchPair[] columnsToAdd, final int numRightBitsToReserve) { this.rightTableReference = new WeakReference<>(rightTable); this.columnsToMatch = columnsToMatch; this.columnsToAdd = columnsToAdd; this.numRightBitsToReserve = numRightBitsToReserve; // precompute hash as right table may disappear int hash = Integer.hashCode(numRightBitsToReserve); hash = 31 * hash + System.identityHashCode(rightTable); hash = 31 * hash + Arrays.hashCode(columnsToMatch); hash = 31 * hash + Arrays.hashCode(columnsToAdd); this.cachedHashCode = hash; } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } final CrossJoin crossJoin = (CrossJoin) other; return equalWeakRefsByReferentIdentity(rightTableReference, crossJoin.rightTableReference) && numRightBitsToReserve == crossJoin.numRightBitsToReserve && Arrays.equals(columnsToMatch, crossJoin.columnsToMatch) && Arrays.equals(columnsToAdd, crossJoin.columnsToAdd); } @Override public int hashCode() { return cachedHashCode; } @Override BaseTable.CopyAttributeOperation copyType() { return BaseTable.CopyAttributeOperation.None; } } public static MemoizedOperationKey crossJoin(final Table rightTable, final MatchPair[] columnsToMatch, final MatchPair[] columnsToAdd, final int numRightBitsToReserve) { return new CrossJoin(rightTable, columnsToMatch, columnsToAdd, numRightBitsToReserve); } private static class RangeJoin extends AttributeAgnosticMemoizedOperationKey { private final WeakReference
rightTableReference; private final JoinMatch[] exactMatches; private final RangeJoinMatch rangeMatch; private final Aggregation[] aggregations; private final int cachedHashCode; private RangeJoin( @NotNull final Table rightTable, @NotNull final Collection exactMatches, @NotNull final RangeJoinMatch rangeMatch, @NotNull final Collection aggregations) { this.rightTableReference = new WeakReference<>(rightTable); this.exactMatches = exactMatches.toArray(JoinMatch[]::new); this.rangeMatch = rangeMatch; this.aggregations = aggregations.toArray(Aggregation[]::new); // precompute hash as right table may disappear int hash = System.identityHashCode(rightTable); hash = 31 * hash + Arrays.hashCode(this.exactMatches); hash = 31 * hash + rangeMatch.hashCode(); hash = 31 * hash + Arrays.hashCode(this.aggregations); this.cachedHashCode = hash; } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } final RangeJoin otherRangeJoin = (RangeJoin) other; return equalWeakRefsByReferentIdentity(rightTableReference, otherRangeJoin.rightTableReference) && Arrays.equals(exactMatches, otherRangeJoin.exactMatches) && rangeMatch.equals(otherRangeJoin.rangeMatch) && Arrays.equals(aggregations, otherRangeJoin.aggregations); } @Override public int hashCode() { return cachedHashCode; } @Override BaseTable.CopyAttributeOperation copyType() { return BaseTable.CopyAttributeOperation.None; } } public static MemoizedOperationKey rangeJoin( @NotNull final Table rightTable, @NotNull final Collection exactMatches, @NotNull final RangeJoinMatch rangeMatch, @NotNull final Collection aggregations) { return new RangeJoin(rightTable, exactMatches, rangeMatch, aggregations); } protected static boolean equalWeakRefsByReferentIdentity(final WeakReference r1, final WeakReference r2) { if (r1 == r2) { return true; } if (r1 == null || r2 == null) { return false; } final Object t1 = r1.get(); final Object t2 = r2.get(); if (t1 == null || t2 == null) { return false; } return t1 == t2; } private static class BlinkToAppendOnly extends AttributeAgnosticMemoizedOperationKey { private final long sizeLimit; private final Object key; private BlinkToAppendOnly(final long sizeLimit, @NotNull final Object key) { this.sizeLimit = sizeLimit; this.key = Objects.requireNonNull(key); } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null || getClass() != other.getClass()) { return false; } final BlinkToAppendOnly blinkToAppendOnly = (BlinkToAppendOnly) other; return sizeLimit == blinkToAppendOnly.sizeLimit && key.equals(blinkToAppendOnly.key); } @Override public int hashCode() { return 31 * key.hashCode() + Long.hashCode(sizeLimit); } @Override BaseTable.CopyAttributeOperation copyType() { return BaseTable.CopyAttributeOperation.None; } } }