From c906306185cf90a6df8dfa44b03af401864dc74c Mon Sep 17 00:00:00 2001 From: Nathaniel Bauernfeind Date: Sat, 24 Feb 2024 00:09:01 -0700 Subject: [PATCH] Stateful Selectables and SelectColumns --- .../deephaven/engine/table/ColumnSource.java | 2 +- .../BaseTableTransformationColumn.java | 5 - .../impl/partitioned/LongConstantColumn.java | 5 - .../table/impl/select/DhFormulaColumn.java | 34 ------- .../table/impl/select/FunctionalColumn.java | 5 - .../select/MultiSourceFunctionalColumn.java | 5 - .../table/impl/select/NullSelectColumn.java | 5 - .../table/impl/select/SelectColumn.java | 20 ++-- .../impl/select/StatefulSelectColumn.java | 95 +++++++++++++++++++ .../select/python/FormulaColumnPython.java | 6 -- .../java/io/deephaven/api/Selectable.java | 18 ++++ .../io/deephaven/api/StatefulSelectable.java | 33 +++++++ 12 files changed, 157 insertions(+), 76 deletions(-) create mode 100644 engine/table/src/main/java/io/deephaven/engine/table/impl/select/StatefulSelectColumn.java create mode 100644 table-api/src/main/java/io/deephaven/api/StatefulSelectable.java diff --git a/engine/api/src/main/java/io/deephaven/engine/table/ColumnSource.java b/engine/api/src/main/java/io/deephaven/engine/table/ColumnSource.java index 9299fcfca7b..ec39de09489 100644 --- a/engine/api/src/main/java/io/deephaven/engine/table/ColumnSource.java +++ b/engine/api/src/main/java/io/deephaven/engine/table/ColumnSource.java @@ -202,7 +202,7 @@ default ColumnSource cast(Class clazz, @Nullable Cl /** * Most column sources will return the same value for a given row without respect to the order that the rows are * read. Those columns sources are considered "stateless" and should return true. - * + *

* Some column sources, however may be dependent on evaluation order. For example, a formula that updates a Map must * be evaluated from the first row to the last row. A column source that has the potential to depend on the order of * evaluation must return false. diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/partitioned/BaseTableTransformationColumn.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/partitioned/BaseTableTransformationColumn.java index be54278bcd9..44a3adf0ab0 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/partitioned/BaseTableTransformationColumn.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/partitioned/BaseTableTransformationColumn.java @@ -59,11 +59,6 @@ public final boolean isRetain() { return false; } - @Override - public final boolean isStateless() { - return true; - } - static ColumnSource getAndValidateInputColumnSource( @NotNull final String inputColumnName, @NotNull final Map> columnsOfInterest) { diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/partitioned/LongConstantColumn.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/partitioned/LongConstantColumn.java index 14d476211bf..dc4ebc4c689 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/partitioned/LongConstantColumn.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/partitioned/LongConstantColumn.java @@ -105,11 +105,6 @@ public final boolean isRetain() { return false; } - @Override - public final boolean isStateless() { - return true; - } - private static final class OutputFormulaFillContext implements Formula.FillContext { private static final Formula.FillContext INSTANCE = new OutputFormulaFillContext(); diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/DhFormulaColumn.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/DhFormulaColumn.java index 4a807eb2c79..64fe9b50b18 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/DhFormulaColumn.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/DhFormulaColumn.java @@ -834,38 +834,4 @@ String makeGetExpression(boolean usePrev) { return String.format("%s.%s(k)", name, getGetterName(columnDefinition.getDataType(), usePrev)); } } - - /** - * Is this parameter immutable, and thus would contribute no state to the formula? - *

- * If any query scope parameter is not a primitive, String, or known immutable class; then it may be a mutable - * object that results in undefined results when the column is not evaluated strictly in order. - * - * @return true if this query scope parameter is immutable - */ - private static boolean isImmutableType(QueryScopeParam param) { - final Object value = param.getValue(); - if (value == null) { - return true; - } - final Class type = value.getClass(); - if (type == String.class || type == BigInteger.class || type == BigDecimal.class - || type == Instant.class || type == ZonedDateTime.class || Table.class.isAssignableFrom(type)) { - return true; - } - // if it is a boxed type, then it is immutable; otherwise we don't know what to do with it - return TypeUtils.isBoxedType(type); - } - - private boolean isUsedColumnStateless(String columnName) { - return columnSources.get(columnName).isStateless(); - } - - @Override - public boolean isStateless() { - return Arrays.stream(params).allMatch(DhFormulaColumn::isImmutableType) - && usedColumns.stream().allMatch(this::isUsedColumnStateless) - && usedColumnArrays.stream().allMatch(this::isUsedColumnStateless); - } - } diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/FunctionalColumn.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/FunctionalColumn.java index 2dda0e38dda..81cf41b1188 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/FunctionalColumn.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/FunctionalColumn.java @@ -218,11 +218,6 @@ public boolean isRetain() { return false; } - @Override - public boolean isStateless() { - return false; - } - @Override public FunctionalColumn copy() { return new FunctionalColumn<>(sourceName, sourceDataType, destName, destDataType, function); diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/MultiSourceFunctionalColumn.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/MultiSourceFunctionalColumn.java index 29d77004a69..cda444ff3ae 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/MultiSourceFunctionalColumn.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/MultiSourceFunctionalColumn.java @@ -197,11 +197,6 @@ public boolean isRetain() { return false; } - @Override - public boolean isStateless() { - return false; - } - @Override public MultiSourceFunctionalColumn copy() { return new MultiSourceFunctionalColumn<>(sourceNames, destName, destDataType, function); diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/NullSelectColumn.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/NullSelectColumn.java index 8a544d80048..1cc33476e85 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/NullSelectColumn.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/NullSelectColumn.java @@ -88,11 +88,6 @@ public boolean isRetain() { return false; } - @Override - public boolean isStateless() { - return true; - } - @Override public SelectColumn copy() { // noinspection unchecked,rawtypes diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/SelectColumn.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/SelectColumn.java index 31295a0e0ea..d578c03c704 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/SelectColumn.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/SelectColumn.java @@ -3,10 +3,7 @@ */ package io.deephaven.engine.table.impl.select; -import io.deephaven.api.ColumnName; -import io.deephaven.api.RawString; -import io.deephaven.api.Selectable; -import io.deephaven.api.Strings; +import io.deephaven.api.*; import io.deephaven.api.expression.Expression; import io.deephaven.api.expression.Function; import io.deephaven.api.expression.Method; @@ -34,6 +31,15 @@ public interface SelectColumn extends Selectable { static SelectColumn of(Selectable selectable) { + if (selectable instanceof StatefulSelectable) { + Selectable inner = ((StatefulSelectable) selectable).innerSelectable(); + + SelectColumn newInner = (inner instanceof SelectColumn) + ? (SelectColumn) selectable + : selectable.expression().walk(new ExpressionAdapter(selectable.newColumn())); + + return new StatefulSelectColumn(newInner); + } return (selectable instanceof SelectColumn) ? (SelectColumn) selectable : selectable.expression().walk(new ExpressionAdapter(selectable.newColumn())); @@ -172,12 +178,6 @@ default void validateSafeForRefresh(final BaseTable sourceTable) { // nothing to validate by default } - /** - * Returns true if this column is stateless (i.e. one row does not depend on the order of evaluation for another - * row). - */ - boolean isStateless(); - /** * Create a copy of this SelectColumn. * diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/StatefulSelectColumn.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/StatefulSelectColumn.java new file mode 100644 index 00000000000..32e925c8d16 --- /dev/null +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/StatefulSelectColumn.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending + */ +package io.deephaven.engine.table.impl.select; + +import io.deephaven.engine.rowset.TrackingRowSet; +import io.deephaven.engine.table.ColumnDefinition; +import io.deephaven.engine.table.ColumnSource; +import io.deephaven.engine.table.WritableColumnSource; +import io.deephaven.engine.table.impl.MatchPair; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +public class StatefulSelectColumn implements SelectColumn { + private final SelectColumn inner; + + public StatefulSelectColumn(final SelectColumn inner) { + this.inner = inner; + } + + @Override + public List initInputs( + @NotNull final TrackingRowSet rowSet, + @NotNull final Map> columnsOfInterest) { + return inner.initInputs(rowSet, columnsOfInterest); + } + + @Override + public List initDef( + @NotNull final Map> columnDefinitionMap) { + return inner.initDef(columnDefinitionMap); + } + + @Override + public Class getReturnedType() { + return inner.getReturnedType(); + } + + @Override + public List getColumns() { + return inner.getColumns(); + } + + @Override + public List getColumnArrays() { + return inner.getColumnArrays(); + } + + @Override + public @NotNull ColumnSource getDataView() { + return inner.getDataView(); + } + + @Override + public @NotNull ColumnSource getLazyView() { + return inner.getLazyView(); + } + + @Override + public String getName() { + return inner.getName(); + } + + @Override + public MatchPair getMatchPair() { + return inner.getMatchPair(); + } + + @Override + public WritableColumnSource newDestInstance(long size) { + return inner.newDestInstance(size); + } + + @Override + public WritableColumnSource newFlatDestInstance(long size) { + return inner.newFlatDestInstance(size); + } + + @Override + public boolean isRetain() { + return inner.isRetain(); + } + + @Override + public SelectColumn copy() { + return new StatefulSelectColumn(inner.copy()); + } + + @Override + public boolean isStateless() { + return false; + } +} diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/python/FormulaColumnPython.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/python/FormulaColumnPython.java index cc84b09739d..8b009fa4eab 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/select/python/FormulaColumnPython.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/select/python/FormulaColumnPython.java @@ -51,12 +51,6 @@ public final List initDef(Map> columnDefinit return usedColumns; } - @Override - public boolean isStateless() { - // we can't control python - return false; - } - @Override protected final FormulaSourceDescriptor getSourceDescriptor() { return new FormulaSourceDescriptor( diff --git a/table-api/src/main/java/io/deephaven/api/Selectable.java b/table-api/src/main/java/io/deephaven/api/Selectable.java index 3fcb1018d84..1a27f4729ad 100644 --- a/table-api/src/main/java/io/deephaven/api/Selectable.java +++ b/table-api/src/main/java/io/deephaven/api/Selectable.java @@ -20,6 +20,16 @@ */ public interface Selectable { + /** + * Wrap a selectable to mark it as stateful. + * + * @param inner the selectable that is stateful + * @return the wrapped selectable + */ + static StatefulSelectable stateful(Selectable inner) { + return new StatefulSelectable(inner); + } + static Selectable of(ColumnName newColumn, Expression expression) { if (newColumn.equals(expression)) { return newColumn; @@ -65,4 +75,12 @@ static List from(Collection values) { * @return the expression */ Expression expression(); + + /** + * @return true if this column is stateless (i.e. one row does not depend on the order of evaluation for another + * row). + */ + default boolean isStateless() { + return true; + } } diff --git a/table-api/src/main/java/io/deephaven/api/StatefulSelectable.java b/table-api/src/main/java/io/deephaven/api/StatefulSelectable.java new file mode 100644 index 00000000000..f97d4526c55 --- /dev/null +++ b/table-api/src/main/java/io/deephaven/api/StatefulSelectable.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2016-2024 Deephaven Data Labs and Patent Pending + */ +package io.deephaven.api; + +import io.deephaven.api.expression.Expression; + +public class StatefulSelectable implements Selectable { + private final Selectable inner; + + public StatefulSelectable(final Selectable inner) { + this.inner = inner; + } + + public Selectable innerSelectable() { + return inner; + } + + @Override + public ColumnName newColumn() { + return inner.newColumn(); + } + + @Override + public Expression expression() { + return inner.expression(); + } + + @Override + public boolean isStateless() { + return false; + } +}