You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by ht...@apache.org on 2026/10/05 05:24:07 UTC
(asterixdb) branch master updated: [ASTERIXDB-3815][COMP]: Expressions in time travel
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git
The following commit(s) were added to refs/heads/master by this push:
new ffbecb6e71 [ASTERIXDB-3815][COMP]: Expressions in time travel
ffbecb6e71 is described below
commit ffbecb6e7182538d7608d1d05449d5b1fefb9d78
Author: Hussain Towaileb <Hu...@Couchbase.com>
AuthorDate: Sat Sep 19 20:37:49 2026 +0300
[ASTERIXDB-3815][COMP]: Expressions in time travel
AT SNAPSHOT and AT TIMESTAMP accepted only a string literal, so a
snapshot could never be derived -- every value had to be computed
outside the query and pasted in as text. The value is now an ordinary
expression: it goes through the language rewrites like any other,
reaches the plan as an expression reference, and is reduced to a
constant before the scan that consumes it is built. A value that does
not reduce to a usable constant is reported rather than ignored.
A time travel value is consumed while the plan is built, before any row
or job exists, so a function there can only mean its value now. That
makes it safe to evaluate every function in that one position, including
the clock functions that constant folding otherwise leaves alone,
without changing what any function means elsewhere. A clock there is
read in UTC, the zone the reader takes a timestamp in, so it names the
current instant whatever the controller's zone is, and a plan holding
such a value is not cached, so every run of the statement reads the
clock again. The reader also accepts a timestamp with a zone offset, the
form a clock rendered in a zone produces. A function that reads the job
start time is reported with the wall clock variant to use instead, taken
from a registry an extension can add its own functions to.
Also fixes two defects that silently dropped a JOIN's time travel, so
the latest snapshot was read instead of the one asked for: one when the
clause was cloned, and one when a JOIN preceding a RIGHT OUTER JOIN was
rebuilt by that rewrite.
Added test cases covering the accepted forms, clock-derived and
stringified values against a three-snapshot table, clocks in a zone
behind UTC, offset timestamps, plan caching, joins, subqueries, function
inlining and the right-join rewrite, the errors for a value that is not
constant, not usable or job-dependent, and the grammar.
Also resolves ASTERIXDB-3819.
Ext-ref: MB-73487
Ext-ref: MB-73488
Generated-by: Claude Opus 5, Claude Fable 5.1 and Claude Opus 5.5 <no...@anthropic.com>
Change-Id: Ibe50c85b5defd86bed540d4c8fd14320f17cdad9
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/21606
Reviewed-by: Ali Alsuliman <al...@gmail.com>
Tested-by: Hussain Towaileb <hu...@gmail.com>
Integration-Tests: Jenkins <je...@fulliautomatix.ics.uci.edu>
---
.../asterix/optimizer/base/RuleCollections.java | 3 +
.../rules/ResolveTimeTravelValueRule.java | 203 +++++++++++++
.../optimizer/rules/util/TimeTravelUtil.java | 150 ++++++++++
.../rules/visitor/ConstantFoldingVisitor.java | 10 +-
.../SqlppExpressionToPlanTranslator.java | 74 ++++-
.../apache/asterix/api/common/APIFramework.java | 2 +-
.../asterix/app/function/DatasetRewriter.java | 20 +-
.../optimizer/rules/util/TimeTravelUtilTest.java | 315 +++++++++++++++++++++
.../time-travel/time-travel-clock-value.sqlpp | 45 +++
.../time-travel/time-travel-expression.sqlpp | 52 ++++
.../time-travel/time-travel-clock-value.plan | 70 +++++
.../time-travel/time-travel-expression.plan | 138 +++++++++
.../time-travel/time-travel-clock-value.plan | 70 +++++
.../time-travel/time-travel-expression.plan | 112 ++++++++
.../bad-snapshot-format/test.090.query.sqlpp} | 21 +-
.../bad-snapshot-format/test.100.query.sqlpp} | 20 +-
.../bad-snapshot-format/test.110.query.sqlpp} | 20 +-
.../iceberg/snapshots/test.085.query.sqlpp} | 22 +-
.../iceberg/snapshots/test.090.query.sqlpp} | 21 +-
.../iceberg/snapshots/test.100.query.sqlpp} | 22 +-
.../iceberg/snapshots/test.110.query.sqlpp} | 21 +-
.../iceberg/snapshots/test.120.query.sqlpp} | 22 +-
.../iceberg/snapshots/test.140.query.sqlpp} | 23 +-
.../iceberg/snapshots/test.150.query.sqlpp} | 23 +-
.../iceberg/snapshots/test.160.query.sqlpp} | 23 +-
.../iceberg/snapshots/test.170.query.sqlpp} | 23 +-
.../iceberg/snapshots/test.180.query.sqlpp} | 21 +-
.../iceberg/snapshots/test.190.query.sqlpp} | 20 +-
.../iceberg/snapshots/test.200.query.sqlpp} | 21 +-
.../iceberg/snapshots/test.210.ddl.sqlpp} | 20 +-
.../iceberg/snapshots/test.220.query.sqlpp} | 20 +-
.../iceberg/snapshots/test.230.ddl.sqlpp} | 18 +-
.../iceberg/snapshots/test.240.query.sqlpp} | 25 +-
.../iceberg/snapshots/test.250.ddl.sqlpp} | 22 +-
.../iceberg/snapshots/test.260.query.sqlpp} | 21 +-
.../iceberg/snapshots/test.270.ddl.sqlpp} | 18 +-
.../iceberg/snapshots/test.280.ddl.sqlpp} | 20 +-
.../iceberg/snapshots/test.290.query.sqlpp} | 21 +-
.../iceberg/snapshots/test.300.ddl.sqlpp} | 19 +-
.../iceberg/snapshots/test.310.query.sqlpp} | 27 +-
.../iceberg/snapshots/test.320.query.sqlpp} | 23 +-
.../iceberg/snapshots/test.330.query.sqlpp} | 24 +-
.../iceberg/snapshots/test.340.query.sqlpp} | 25 +-
.../iceberg/snapshots/test.360.query.sqlpp} | 24 +-
.../iceberg/snapshots/test.370.query.sqlpp} | 22 +-
.../iceberg/snapshots/test.380.query.sqlpp} | 20 +-
.../iceberg/snapshots/test.390.query.sqlpp} | 21 +-
.../iceberg/snapshots/test.400.query.sqlpp} | 20 +-
.../iceberg/snapshots/test.410.query.sqlpp} | 20 +-
.../iceberg/snapshots/test.420.query.sqlpp} | 20 +-
.../iceberg/snapshots/test.430.query.sqlpp} | 21 +-
.../iceberg/snapshots/test.440.query.sqlpp | 30 ++
.../iceberg/snapshots/test.450.query.sqlpp} | 21 +-
.../iceberg/snapshots/test.455.ddl.sqlpp} | 21 +-
.../iceberg/snapshots/test.460.query.sqlpp} | 19 +-
.../iceberg/snapshots/test.470.query.sqlpp} | 19 +-
.../iceberg/snapshots/test.480.cachedplan.sqlpp} | 21 +-
.../iceberg/snapshots/test.490.cachedplan.sqlpp} | 21 +-
.../iceberg/snapshots/test.495.ddl.sqlpp} | 18 +-
.../plan-cache-time-travel.001.delete.http} | 19 +-
.../plan-cache-time-travel.002.ddl.sqlpp} | 22 +-
.../plan-cache-time-travel.003.cachedplan.sqlpp} | 21 +-
.../plan-cache-time-travel.004.cachedplan.sqlpp} | 21 +-
.../plan-cache-time-travel.005.cachedplan.sqlpp} | 20 +-
.../plan-cache-time-travel.006.cachedplan.sqlpp} | 20 +-
.../plan-cache-time-travel.007.cachedplan.sqlpp} | 21 +-
.../plan-cache-time-travel.008.cachedplan.sqlpp} | 21 +-
.../plan-cache-time-travel.009.ddl.sqlpp} | 18 +-
.../time-travel-negative.1.ddl.sqlpp} | 22 +-
.../time-travel-negative.10.query.sqlpp} | 21 +-
.../time-travel-negative.11.query.sqlpp} | 21 +-
.../time-travel-negative.12.query.sqlpp} | 23 +-
.../time-travel-negative.13.query.sqlpp} | 21 +-
.../time-travel-negative.14.query.sqlpp} | 21 +-
.../time-travel-negative.15.query.sqlpp} | 23 +-
.../time-travel-negative.16.ddl.sqlpp} | 18 +-
.../time-travel-negative.2.query.sqlpp} | 22 +-
.../time-travel-negative.3.query.sqlpp} | 21 +-
.../time-travel-negative.4.query.sqlpp} | 21 +-
.../time-travel-negative.5.query.sqlpp} | 21 +-
.../time-travel-negative.6.query.sqlpp} | 21 +-
.../time-travel-negative.7.query.sqlpp} | 21 +-
.../time-travel-negative.8.query.sqlpp} | 22 +-
.../time-travel-negative.9.query.sqlpp} | 21 +-
.../time-travel-non-assign.1.ddl.sqlpp} | 23 +-
.../time-travel-non-assign.2.query.sqlpp} | 20 +-
.../time-travel-non-assign.3.query.sqlpp} | 20 +-
.../time-travel-non-assign.4.query.sqlpp} | 20 +-
.../time-travel-non-assign.5.query.sqlpp} | 21 +-
.../time-travel-non-assign.6.query.sqlpp} | 21 +-
.../time-travel-non-assign.7.ddl.sqlpp} | 18 +-
.../time-travel-udf-param.1.ddl.sqlpp} | 25 +-
.../time-travel-udf-param.2.query.sqlpp} | 20 +-
.../time-travel-udf-param.3.query.sqlpp} | 20 +-
.../time-travel-udf-param.4.query.sqlpp} | 21 +-
.../time-travel-udf-param.5.ddl.sqlpp} | 18 +-
.../time-travel-value-types.1.ddl.sqlpp} | 23 +-
.../time-travel-value-types.2.query.sqlpp} | 20 +-
.../time-travel-value-types.3.query.sqlpp} | 20 +-
.../time-travel-value-types.4.query.sqlpp} | 21 +-
.../time-travel-value-types.5.query.sqlpp} | 21 +-
.../time-travel-value-types.6.query.sqlpp} | 22 +-
.../time-travel-value-types.7.ddl.sqlpp} | 18 +-
.../results/iceberg/snapshots/result.085.adm | 1 +
.../results/iceberg/snapshots/result.090.adm | 1 +
.../results/iceberg/snapshots/result.100.adm | 1 +
.../results/iceberg/snapshots/result.110.adm | 1 +
.../results/iceberg/snapshots/result.120.adm | 1 +
.../results/iceberg/snapshots/result.140.adm | 1 +
.../results/iceberg/snapshots/result.150.adm | 1 +
.../results/iceberg/snapshots/result.160.adm | 1 +
.../results/iceberg/snapshots/result.170.adm | 1 +
.../results/iceberg/snapshots/result.180.adm | 1 +
.../results/iceberg/snapshots/result.190.adm | 1 +
.../results/iceberg/snapshots/result.200.adm | 1 +
.../results/iceberg/snapshots/result.220.adm | 1 +
.../results/iceberg/snapshots/result.240.adm | 1 +
.../results/iceberg/snapshots/result.260.adm | 1 +
.../results/iceberg/snapshots/result.290.adm | 1 +
.../results/iceberg/snapshots/result.310.adm | 2 +
.../results/iceberg/snapshots/result.320.adm | 1 +
.../results/iceberg/snapshots/result.330.adm | 1 +
.../results/iceberg/snapshots/result.340.adm | 1 +
.../results/iceberg/snapshots/result.360.adm | 1 +
.../results/iceberg/snapshots/result.370.adm | 1 +
.../results/iceberg/snapshots/result.380.adm | 1 +
.../results/iceberg/snapshots/result.390.adm | 1 +
.../results/iceberg/snapshots/result.400.adm | 1 +
.../results/iceberg/snapshots/result.410.adm | 1 +
.../results/iceberg/snapshots/result.420.adm | 1 +
.../results/iceberg/snapshots/result.430.adm | 1 +
.../results/iceberg/snapshots/result.440.adm | 1 +
.../results/iceberg/snapshots/result.450.adm | 1 +
.../results/iceberg/snapshots/result.460.adm | 1 +
.../results/iceberg/snapshots/result.470.adm | 1 +
.../results/iceberg/snapshots/result.480.regexadm | 1 +
.../results/iceberg/snapshots/result.490.regexadm | 1 +
.../plan-cache-time-travel.001.regexjson | 1 +
.../plan-cache-time-travel.003.regexadm | 1 +
.../plan-cache-time-travel.004.regexadm | 1 +
.../plan-cache-time-travel.005.regexadm | 1 +
.../plan-cache-time-travel.006.regexadm | 1 +
.../plan-cache-time-travel.007.regexadm | 1 +
.../plan-cache-time-travel.008.regexadm | 1 +
.../time-travel-non-assign.2.adm | 1 +
.../time-travel-non-assign.3.adm | 1 +
.../time-travel-non-assign.4.adm | 1 +
.../time-travel-udf-param.2.adm | 1 +
.../time-travel-udf-param.3.adm | 1 +
.../time-travel-value-types.2.adm | 1 +
.../time-travel-value-types.3.adm | 1 +
.../src/test/resources/runtimets/sqlpp_queries.xml | 45 +++
.../test/resources/runtimets/testsuite_iceberg.xml | 11 +-
.../asterix/common/exceptions/ErrorCode.java | 3 +
.../src/main/resources/asx_errormsg/en.properties | 3 +
.../src/main/markdown/builtins/7_temporal.md | 51 +++-
.../util/iceberg/IcebergSnapshotUtils.java | 13 +-
.../lang/common/util/CommonFunctionMapUtil.java | 9 +
.../asterix/lang/sqlpp/JoinClauseRightInput.java | 8 +-
.../clause/AbstractBinaryCorrelateClause.java | 12 +-
...AbstractBinaryCorrelateWithConditionClause.java | 4 +-
.../apache/asterix/lang/sqlpp/clause/FromTerm.java | 12 +-
.../asterix/lang/sqlpp/clause/JoinClause.java | 4 +-
.../visitor/SqlppGatherFunctionCallsVisitor.java | 9 +
.../rewrites/visitor/SqlppInlineUdfsVisitor.java | 14 +-
.../visitor/SqlppRightJoinRewriteVisitor.java | 16 +-
.../asterix/lang/sqlpp/struct/TimeTravelSpec.java | 97 +++++++
.../lang/sqlpp/visitor/DeepCopyVisitor.java | 16 +-
.../lang/sqlpp/visitor/FreeVariableVisitor.java | 15 +
.../lang/sqlpp/visitor/SqlppAstPrintVisitor.java | 6 +-
.../SqlppCloneAndSubstituteVariablesVisitor.java | 27 +-
.../sqlpp/visitor/SqlppFormatPrintVisitor.java | 6 +-
.../AbstractSqlppContainsExpressionVisitor.java | 11 +-
.../AbstractSqlppExpressionScopingVisitor.java | 12 +
.../base/AbstractSqlppSimpleExpressionVisitor.java | 15 +
.../asterix-lang-sqlpp/src/main/javacc/SQLPP.jj | 87 +++---
.../lang/sqlpp/parser/TimeTravelParserTest.java | 295 +++++++++++++++++++
.../asterix/om/functions/BuiltinFunctions.java | 22 ++
.../temporal/AbstractCurrentTemporalValueEval.java | 23 +-
.../functions/temporal/CurrentDateEval.java | 3 +-
.../temporal/CurrentDateImmediateEval.java | 3 +-
.../functions/temporal/CurrentDateTimeEval.java | 3 +-
.../temporal/CurrentDateTimeImmediateEval.java | 3 +-
.../temporal/CurrentTemporalValueUtil.java | 60 ++++
.../functions/temporal/CurrentTimeEval.java | 3 +-
.../temporal/CurrentTimeImmediateEval.java | 3 +-
.../core/algebra/base/IOptimizationContext.java | 11 +
.../logical/AbstractUnnestNonMapOperator.java | 16 ++
.../core/algebra/operators/logical/TimeTravel.java | 33 ++-
...calOperatorDeepCopyWithNewVariablesVisitor.java | 12 +-
.../logical/visitors/OperatorDeepCopyVisitor.java | 11 +-
.../LogicalOperatorPrettyPrintVisitor.java | 4 +-
.../base/AlgebricksOptimizationContext.java | 11 +
.../core/utils/LogicalOperatorDotVisitor.java | 4 +-
194 files changed, 2568 insertions(+), 1650 deletions(-)
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
index 7352bef9c9..d5b835b8a0 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
@@ -94,6 +94,7 @@ import org.apache.asterix.optimizer.rules.RemoveRedundantSelectRule;
import org.apache.asterix.optimizer.rules.RemoveSortInFeedIngestionRule;
import org.apache.asterix.optimizer.rules.RemoveUnknownCheckForKnownTypeExpressionRule;
import org.apache.asterix.optimizer.rules.RemoveUnusedOneToOneEquiJoinRule;
+import org.apache.asterix.optimizer.rules.ResolveTimeTravelValueRule;
import org.apache.asterix.optimizer.rules.RewriteClusterByToKMeansRule;
import org.apache.asterix.optimizer.rules.RewriteCountDistinctToHashRule;
import org.apache.asterix.optimizer.rules.RewriteDistinctAggregateRule;
@@ -241,6 +242,8 @@ public final class RuleCollections {
normalization.add(new ListifyUnnestingFunctionRule());
normalization.add(new RemoveRedundantSelectRule());
normalization.add(new CancelUnnestSingletonListRule());
+ // must run before UnnestToDataScanRule, which is where a time travel value is read
+ normalization.add(new ResolveTimeTravelValueRule(appCtx));
normalization.add(new UnnestToDataScanRule());
normalization.add(new MetaFunctionToMetaVariableRule());
normalization.add(new FuzzyEqRule());
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/ResolveTimeTravelValueRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/ResolveTimeTravelValueRule.java
new file mode 100644
index 0000000000..318f147e3e
--- /dev/null
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/ResolveTimeTravelValueRule.java
@@ -0,0 +1,203 @@
+/*
+ * 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.asterix.optimizer.rules;
+
+import java.time.ZoneOffset;
+import java.time.zone.ZoneRules;
+import java.util.List;
+
+import org.apache.asterix.common.dataflow.ICcApplicationContext;
+import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.lang.common.util.CommonFunctionMapUtil;
+import org.apache.asterix.om.base.ADate;
+import org.apache.asterix.om.base.ADateTime;
+import org.apache.asterix.om.base.ATime;
+import org.apache.asterix.om.base.IAObject;
+import org.apache.asterix.om.constants.AsterixConstantValue;
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.optimizer.rules.visitor.ConstantFoldingVisitor;
+import org.apache.asterix.runtime.evaluators.functions.temporal.CurrentTemporalValueUtil;
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
+import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractUnnestNonMapOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
+import org.apache.hyracks.algebricks.core.rewriter.base.IAlgebraicRewriteRule;
+import org.apache.hyracks.api.exceptions.SourceLocation;
+import org.apache.hyracks.util.LogRedactionUtil;
+
+/**
+ * Reduces an {@code AT SNAPSHOT} / {@code AT TIMESTAMP} value to a constant, evaluating every function in it.
+ * <p>
+ * The value selects which data files the scan reads, so it is consumed while the plan is being built: there
+ * are no rows and no job yet, and the only thing a function can mean in this position is "the value now". That
+ * makes it safe to evaluate functions here that ordinary constant folding refuses -- the clock functions above
+ * all -- without changing what they mean anywhere else, because nothing about the functions themselves changes.
+ * Only this one expression is treated this way.
+ * <p>
+ * A value that does not reduce is reported here, naming what could not be resolved -- and, for the plain
+ * clock functions, the {@code _immediate} variant that can be. Variable references are never evaluated; the
+ * translator has already rejected any that could occur here.
+ * <p>
+ * Runs after {@code ConstantFoldingRule} and before {@code UnnestToDataScanRule}, where the value is read.
+ */
+public class ResolveTimeTravelValueRule implements IAlgebraicRewriteRule {
+
+ private final ConstantFoldingVisitor cfv;
+
+ public ResolveTimeTravelValueRule(ICcApplicationContext appCtx) {
+ cfv = new TimeTravelValueFoldingVisitor(appCtx);
+ }
+
+ @Override
+ public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
+ throws AlgebricksException {
+ ILogicalOperator op = opRef.getValue();
+ if (!(op instanceof AbstractUnnestNonMapOperator)) {
+ return false;
+ }
+ TimeTravel timeTravel = ((AbstractUnnestNonMapOperator) op).getTimeTravel();
+ if (timeTravel == null || timeTravel.getValueExpression().getExpressionTag() == LogicalExpressionTag.CONSTANT) {
+ return false;
+ }
+ cfv.reset(context);
+ Mutable<ILogicalExpression> valueRef = timeTravel.getValueExpressionRef();
+ if (!valueRef.getValue().isFunctional()) {
+ // the folded value is only right for this compilation; a cached plan would keep reading it on
+ // every later run of the statement, e.g. a clock read hours ago
+ context.markPlanNotReusable();
+ }
+ replaceWallClockCalls(valueRef, System.currentTimeMillis());
+ // a function that needs state only a running job has fails with a checked exception, which constant
+ // folding absorbs by leaving the call unfolded; it is reported below as unresolved
+ cfv.transform(valueRef);
+ ILogicalExpression value = valueRef.getValue();
+ if (value.getExpressionTag() == LogicalExpressionTag.CONSTANT) {
+ return true;
+ }
+ SourceLocation sourceLoc =
+ value.getSourceLocation() != null ? value.getSourceLocation() : op.getSourceLocation();
+ FunctionIdentifier jobClock = findJobClockCall(value);
+ if (jobClock != null) {
+ throw new CompilationException(ErrorCode.TIME_TRAVEL_VALUE_UNSUPPORTED_FUNCTION, sourceLoc,
+ sqlppNameWithAliases(jobClock), sqlppName(BuiltinFunctions.getImmediateVariant(jobClock)));
+ }
+ throw new CompilationException(ErrorCode.TIME_TRAVEL_VALUE_NOT_RESOLVED, sourceLoc,
+ LogRedactionUtil.userData(value.toString()));
+ }
+
+ /**
+ * The wall clock functions take their zone from the job, which does not exist yet, so their evaluators cannot
+ * run here. The value is read as UTC, so the clock is read in UTC: in the controller's own zone it would name
+ * an instant off by that zone's offset.
+ */
+ private static void replaceWallClockCalls(Mutable<ILogicalExpression> exprRef, long nowMillis) {
+ ILogicalExpression expr = exprRef.getValue();
+ if (expr.getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) {
+ return;
+ }
+ AbstractFunctionCallExpression call = (AbstractFunctionCallExpression) expr;
+ IAObject now = wallClockInUtc(call.getFunctionIdentifier(), nowMillis);
+ if (now != null) {
+ ConstantExpression constant = new ConstantExpression(new AsterixConstantValue(now));
+ constant.setSourceLocation(call.getSourceLocation());
+ exprRef.setValue(constant);
+ return;
+ }
+ for (Mutable<ILogicalExpression> arg : call.getArguments()) {
+ replaceWallClockCalls(arg, nowMillis);
+ }
+ }
+
+ /**
+ * @return the value {@code fid} would produce at {@code nowMillis} in UTC, or {@code null} if {@code fid} is
+ * not a wall clock function
+ */
+ public static IAObject wallClockInUtc(FunctionIdentifier fid, long nowMillis) {
+ ZoneRules utc = ZoneOffset.UTC.getRules();
+ if (fid.equals(BuiltinFunctions.CURRENT_DATETIME_IMMEDIATE)) {
+ return new ADateTime(CurrentTemporalValueUtil.valueAt(ATypeTag.DATETIME, nowMillis, utc));
+ } else if (fid.equals(BuiltinFunctions.CURRENT_DATE_IMMEDIATE)) {
+ return new ADate((int) CurrentTemporalValueUtil.valueAt(ATypeTag.DATE, nowMillis, utc));
+ } else if (fid.equals(BuiltinFunctions.CURRENT_TIME_IMMEDIATE)) {
+ return new ATime((int) CurrentTemporalValueUtil.valueAt(ATypeTag.TIME, nowMillis, utc));
+ }
+ return null;
+ }
+
+ /**
+ * A job clock function reads the job start time. A job does not exist while a time travel value is resolved,
+ * so it can never be reduced here; the immediate variant registered for it reads the wall clock and can.
+ */
+ private static FunctionIdentifier findJobClockCall(ILogicalExpression expr) {
+ if (expr.getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) {
+ return null;
+ }
+ AbstractFunctionCallExpression call = (AbstractFunctionCallExpression) expr;
+ if (BuiltinFunctions.getImmediateVariant(call.getFunctionIdentifier()) != null) {
+ return call.getFunctionIdentifier();
+ }
+ for (Mutable<ILogicalExpression> arg : call.getArguments()) {
+ FunctionIdentifier found = findJobClockCall(arg.getValue());
+ if (found != null) {
+ return found;
+ }
+ }
+ return null;
+ }
+
+ private static String sqlppName(FunctionIdentifier fid) {
+ return fid.getName().replace('-', '_');
+ }
+
+ /**
+ * The call may have been written under an alias, which is resolved away before this rule runs, so every
+ * spelling that leads to the function is named.
+ */
+ private static String sqlppNameWithAliases(FunctionIdentifier fid) {
+ String name = sqlppName(fid);
+ List<String> aliases =
+ CommonFunctionMapUtil.getAliases(fid.getName()).stream().filter(alias -> !alias.equals(name)).toList();
+ return aliases.isEmpty() ? name : name + " (or " + String.join(", ", aliases) + ")";
+ }
+
+ /**
+ * Constant folding that evaluates every call whose arguments are constants, functional or not.
+ */
+ private static final class TimeTravelValueFoldingVisitor extends ConstantFoldingVisitor {
+
+ TimeTravelValueFoldingVisitor(ICcApplicationContext appCtx) {
+ super(appCtx);
+ }
+
+ @Override
+ protected boolean isFoldable(ScalarFunctionCallExpression expr) {
+ return true;
+ }
+ }
+}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/util/TimeTravelUtil.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/util/TimeTravelUtil.java
new file mode 100644
index 0000000000..306fb9b4d3
--- /dev/null
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/util/TimeTravelUtil.java
@@ -0,0 +1,150 @@
+/*
+ * 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.asterix.optimizer.rules.util;
+
+import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
+import static java.time.temporal.ChronoField.HOUR_OF_DAY;
+import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
+import static java.time.temporal.ChronoField.NANO_OF_SECOND;
+import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+
+import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.om.base.ADate;
+import org.apache.asterix.om.base.ADateTime;
+import org.apache.asterix.om.base.AInt16;
+import org.apache.asterix.om.base.AInt32;
+import org.apache.asterix.om.base.AInt64;
+import org.apache.asterix.om.base.AInt8;
+import org.apache.asterix.om.base.AString;
+import org.apache.asterix.om.base.IAObject;
+import org.apache.asterix.om.constants.AsterixConstantValue;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
+import org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.IAlgebricksConstantValue;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
+import org.apache.hyracks.api.exceptions.SourceLocation;
+
+/**
+ * Reads the constant an {@code AT SNAPSHOT} / {@code AT TIMESTAMP} value folded down to, and renders it in one
+ * of the forms the Iceberg reader parses.
+ * <p>
+ * The value rides the ordinary expression machinery: it is an expression reference on the unnest operator, so
+ * constant folding reduces it during normalization -- before {@code UnnestToDataScanRule} turns the unnest into
+ * the scan that consumes it. What is left here is only reading the result and rejecting what could not be used:
+ * the snapshot selects which manifest and data files the scan reads, so there is no runtime evaluation path for
+ * it and it has to be a constant by the time the scan is built.
+ *
+ * @see org.apache.asterix.external.util.iceberg.IcebergSnapshotUtils
+ */
+public final class TimeTravelUtil {
+
+ private static final String EXPECTED_FOR_SNAPSHOT_ID = "an integer or a string";
+
+ private static final String EXPECTED_FOR_SNAPSHOT_TIMESTAMP = "an integer, a string, a date or a datetime";
+
+ /**
+ * One of the forms {@code IcebergSnapshotUtils} accepts: always seconds, fractional part only when there is
+ * one. {@link DateTimeFormatter#ISO_LOCAL_DATE_TIME} would drop {@code :00} seconds, which still parses but
+ * makes the value look truncated.
+ */
+ private static final DateTimeFormatter SNAPSHOT_TIMESTAMP_FORMAT =
+ new DateTimeFormatterBuilder().append(ISO_LOCAL_DATE).appendLiteral('T').appendValue(HOUR_OF_DAY, 2)
+ .appendLiteral(':').appendValue(MINUTE_OF_HOUR, 2).appendLiteral(':')
+ .appendValue(SECOND_OF_MINUTE, 2).appendFraction(NANO_OF_SECOND, 0, 3, true).toFormatter();
+
+ private TimeTravelUtil() {
+ }
+
+ /**
+ * @param timeTravel the specification carried by the unnest operator
+ * @param sourceLoc where to report a failure against
+ * @return the value in the string form the Iceberg reader parses
+ * @throws CompilationException if the value did not fold to a constant, or folded to something unusable
+ */
+ public static String resolve(TimeTravel timeTravel, SourceLocation sourceLoc) throws CompilationException {
+ ILogicalExpression valueExpr = timeTravel.getValueExpression();
+ if (valueExpr.getExpressionTag() != LogicalExpressionTag.CONSTANT) {
+ throw new CompilationException(ErrorCode.EXPECTED_CONSTANT_VALUE, sourceLoc);
+ }
+ IAlgebricksConstantValue constantValue = ((ConstantExpression) valueExpr).getValue();
+ if (constantValue.isNull() || constantValue.isMissing()) {
+ // e.g. 'date("2026-08-20") - 1': date arithmetic only accepts durations, and a type mismatch
+ // inside an arithmetic function produces null rather than an error.
+ throw invalidValue(timeTravel.getType(), sourceLoc, constantValue.isNull() ? "null" : "missing");
+ }
+ if (!(constantValue instanceof AsterixConstantValue)) {
+ throw new CompilationException(ErrorCode.EXPECTED_CONSTANT_VALUE, sourceLoc);
+ }
+ return stringify(((AsterixConstantValue) constantValue).getObject(), timeTravel.getType(), sourceLoc);
+ }
+
+ public static String stringify(IAObject value, TimeTravel.Type type, SourceLocation sourceLoc)
+ throws CompilationException {
+ ATypeTag typeTag = value.getType().getTypeTag();
+ switch (typeTag) {
+ case TINYINT:
+ return String.valueOf(((AInt8) value).getByteValue());
+ case SMALLINT:
+ return String.valueOf(((AInt16) value).getShortValue());
+ case INTEGER:
+ return String.valueOf(((AInt32) value).getIntegerValue());
+ case BIGINT:
+ return String.valueOf(((AInt64) value).getLongValue());
+ case STRING:
+ return ((AString) value).getStringValue();
+ case DATE:
+ requireTimestamp(type, sourceLoc, typeTag);
+ return LocalDate.ofEpochDay(((ADate) value).getChrononTimeInDays()).format(ISO_LOCAL_DATE);
+ case DATETIME:
+ requireTimestamp(type, sourceLoc, typeTag);
+ return LocalDateTime
+ .ofInstant(Instant.ofEpochMilli(((ADateTime) value).getChrononTime()), ZoneOffset.UTC)
+ .format(SNAPSHOT_TIMESTAMP_FORMAT);
+ default:
+ throw invalidValue(type, sourceLoc, typeTag.toString().toLowerCase());
+ }
+ }
+
+ /**
+ * A date or datetime only makes sense for {@code AT TIMESTAMP}; a snapshot id is an integer.
+ */
+ private static void requireTimestamp(TimeTravel.Type type, SourceLocation sourceLoc, ATypeTag typeTag)
+ throws CompilationException {
+ if (type != TimeTravel.Type.SNAPSHOT_TIMESTAMP) {
+ throw invalidValue(type, sourceLoc, typeTag.toString().toLowerCase());
+ }
+ }
+
+ private static CompilationException invalidValue(TimeTravel.Type type, SourceLocation sourceLoc, String actual) {
+ String expected =
+ type == TimeTravel.Type.SNAPSHOT_TIMESTAMP ? EXPECTED_FOR_SNAPSHOT_TIMESTAMP : EXPECTED_FOR_SNAPSHOT_ID;
+ return new CompilationException(ErrorCode.INVALID_TIME_TRAVEL_VALUE, sourceLoc, type.getKeyword().toUpperCase(),
+ expected, actual);
+ }
+}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/visitor/ConstantFoldingVisitor.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/visitor/ConstantFoldingVisitor.java
index 5611f94357..230bd6a055 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/visitor/ConstantFoldingVisitor.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/visitor/ConstantFoldingVisitor.java
@@ -215,7 +215,7 @@ public class ConstantFoldingVisitor implements ILogicalExpressionVisitor<Pair<Bo
return Pair.of(changed, expr);
}
- if (!expr.isFunctional() || !canConstantFold(expr)) {
+ if (!isFoldable(expr) || !canConstantFold(expr)) {
return Pair.of(changed, expr);
}
@@ -372,6 +372,14 @@ public class ConstantFoldingVisitor implements ILogicalExpressionVisitor<Pair<Bo
return n;
}
+ /**
+ * Whether a call whose arguments are all constants may be evaluated here. Folding a non-functional call
+ * would fix a value that is meant to vary per evaluation, so by default only functional calls qualify.
+ */
+ protected boolean isFoldable(ScalarFunctionCallExpression expr) {
+ return expr.isFunctional();
+ }
+
private boolean canConstantFold(ScalarFunctionCallExpression function) throws AlgebricksException {
// skip external functions because they're not available at compile time (on CC)
IFunctionInfo fi = function.getFunctionInfo();
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
index 736818cac6..3a395dafec 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SqlppExpressionToPlanTranslator.java
@@ -86,6 +86,7 @@ import org.apache.asterix.lang.sqlpp.optype.SetOpType;
import org.apache.asterix.lang.sqlpp.optype.UnnestType;
import org.apache.asterix.lang.sqlpp.struct.SetOperationInput;
import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
import org.apache.asterix.lang.sqlpp.util.SqlppRewriteUtil;
import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
@@ -139,6 +140,7 @@ import org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperato
import org.apache.hyracks.algebricks.core.algebra.operators.logical.ProjectOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.SelectOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.SubplanOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.WindowOperator;
import org.apache.hyracks.algebricks.core.algebra.plan.ALogicalPlanImpl;
@@ -427,10 +429,10 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
LogicalVariable pVar = context.newVarFromExpression(fromTerm.getPositionalVariable());
// We set the positional variable type as BIGINT type.
unnestOp = new UnnestOperator(fromVar, new MutableObject<>(pUnnestExpr.getLeft()), pVar, BuiltinType.AINT64,
- fromTerm.getTimeTravel());
+ toTimeTravel(fromTerm.getTimeTravel()));
} else {
- unnestOp =
- new UnnestOperator(fromVar, new MutableObject<>(pUnnestExpr.getLeft()), fromTerm.getTimeTravel());
+ unnestOp = new UnnestOperator(fromVar, new MutableObject<>(pUnnestExpr.getLeft()),
+ toTimeTravel(fromTerm.getTimeTravel()));
}
unnestOp.getAnnotations().put(ARRAY_ACCESS, fromExpr.getKind() == Kind.FIELD_ACCESSOR_EXPRESSION);
ExternalSubpathAnnotation hint = ((AbstractExpression) fromExpr).findHint(ExternalSubpathAnnotation.class);
@@ -450,6 +452,62 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
return Pair.of(topOpRef.getValue(), fromVar);
}
+ /**
+ * Translates the value of an {@code AT SNAPSHOT} / {@code AT TIMESTAMP} specification into the expression
+ * reference the operator carries. It is translated against a throwaway input, so a value that needs
+ * operators of its own -- a subquery, or a non-deterministic call that cannot be inlined -- is rejected
+ * here rather than leaking an operator into the plan. Reducing it to a constant is left to constant
+ * folding, and reading that constant to {@code DatasetRewriter}.
+ */
+ private TimeTravel toTimeTravel(TimeTravelSpec timeTravelSpec) throws CompilationException {
+ if (timeTravelSpec == null) {
+ return null;
+ }
+ Mutable<ILogicalOperator> valueInputRef = new MutableObject<>(new EmptyTupleSourceOperator());
+ Pair<ILogicalExpression, Mutable<ILogicalOperator>> eo =
+ langExprToAlgExpression(timeTravelSpec.getValueExpression(), valueInputRef);
+ // A non-functional call is not inlined by langExprToAlgExpression; it is left in an ASSIGN above the
+ // input. There is no plan for that ASSIGN to live in here, so fold its expression back into the value.
+ // Anything else that needed an operator is a subquery, and any variable still referenced afterwards
+ // comes from an enclosing scope; neither can be a constant.
+ ILogicalExpression value = eo.getLeft();
+ Mutable<ILogicalOperator> opRef = eo.getRight();
+ while (opRef != valueInputRef) {
+ ILogicalOperator op = opRef.getValue();
+ if (op.getOperatorTag() != LogicalOperatorTag.ASSIGN) {
+ throw new CompilationException(ErrorCode.EXPECTED_CONSTANT_VALUE, timeTravelSpec.getSourceLocation());
+ }
+ AssignOperator assign = (AssignOperator) op;
+ for (int i = 0, n = assign.getVariables().size(); i < n; i++) {
+ value = substituteVariable(value, assign.getVariables().get(i),
+ assign.getExpressions().get(i).getValue());
+ }
+ opRef = assign.getInputs().get(0);
+ }
+ List<LogicalVariable> usedVars = new ArrayList<>();
+ value.getUsedVariables(usedVars);
+ if (!usedVars.isEmpty()) {
+ throw new CompilationException(ErrorCode.EXPECTED_CONSTANT_VALUE, timeTravelSpec.getSourceLocation());
+ }
+ return new TimeTravel(new MutableObject<>(value), timeTravelSpec.getType());
+ }
+
+ private static ILogicalExpression substituteVariable(ILogicalExpression expr, LogicalVariable var,
+ ILogicalExpression replacement) {
+ switch (expr.getExpressionTag()) {
+ case VARIABLE:
+ return ((VariableReferenceExpression) expr).getVariableReference().equals(var)
+ ? replacement.cloneExpression() : expr;
+ case FUNCTION_CALL:
+ for (Mutable<ILogicalExpression> argRef : ((AbstractFunctionCallExpression) expr).getArguments()) {
+ argRef.setValue(substituteVariable(argRef.getValue(), var, replacement));
+ }
+ return expr;
+ default:
+ return expr;
+ }
+ }
+
@Override
public Pair<ILogicalOperator, LogicalVariable> visit(JoinClause joinClause, Mutable<ILogicalOperator> leftInputRef)
throws CompilationException {
@@ -676,21 +734,21 @@ public class SqlppExpressionToPlanTranslator extends LangExpressionToPlanTransla
Pair<ILogicalExpression, Mutable<ILogicalOperator>> eo = langExprToAlgExpression(rightExpr, inputOpRef);
Pair<ILogicalExpression, Mutable<ILogicalOperator>> pUnnestExpr =
makeUnnestExpression(eo.getLeft(), eo.getRight());
+ TimeTravel timeTravel = toTimeTravel(binaryCorrelate.getTimeTravel());
AbstractUnnestOperator unnestOp;
if (binaryCorrelate.hasPositionalVariable()) {
LogicalVariable pVar = context.newVarFromExpression(binaryCorrelate.getPositionalVariable());
// We set the positional variable type as BIGINT type.
unnestOp = outerUnnest
? new LeftOuterUnnestOperator(rightVar, new MutableObject<>(pUnnestExpr.getLeft()), pVar,
- BuiltinType.AINT64, outerUnnestMissingValue, binaryCorrelate.getTimeTravel())
+ BuiltinType.AINT64, outerUnnestMissingValue, timeTravel)
: new UnnestOperator(rightVar, new MutableObject<>(pUnnestExpr.getLeft()), pVar, BuiltinType.AINT64,
- binaryCorrelate.getTimeTravel());
+ timeTravel);
} else {
unnestOp = outerUnnest
? new LeftOuterUnnestOperator(rightVar, new MutableObject<>(pUnnestExpr.getLeft()),
- outerUnnestMissingValue, binaryCorrelate.getTimeTravel())
- : new UnnestOperator(rightVar, new MutableObject<>(pUnnestExpr.getLeft()),
- binaryCorrelate.getTimeTravel());
+ outerUnnestMissingValue, timeTravel)
+ : new UnnestOperator(rightVar, new MutableObject<>(pUnnestExpr.getLeft()), timeTravel);
}
unnestOp.getAnnotations().put(ARRAY_ACCESS, rightExpr.getKind() == Kind.FIELD_ACCESSOR_EXPRESSION);
ExternalSubpathAnnotation hint = ((AbstractExpression) rightExpr).findHint(ExternalSubpathAnnotation.class);
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
index f2dabacd0f..8b0472e482 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
@@ -451,7 +451,7 @@ public class APIFramework {
if (isQuery && conf.is(SessionConfig.OOB_HYRACKS_JOB)) {
generateJob(spec, output.config().getHyracksJobFormat());
}
- if (isQuery) {
+ if (isQuery && compiler.getOptimizationContext().isPlanReusable()) {
lastCompiledPlan = new CompiledPlan(plan, compiler.getOptimizationContext());
}
return spec;
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/DatasetRewriter.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/DatasetRewriter.java
index 6543601027..0ccb371f7e 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/DatasetRewriter.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/DatasetRewriter.java
@@ -49,6 +49,7 @@ import org.apache.asterix.om.types.IAType;
import org.apache.asterix.om.utils.ConstantExpressionUtil;
import org.apache.asterix.optimizer.rules.UnnestToDataScanRule;
import org.apache.asterix.optimizer.rules.util.EquivalenceClassUtils;
+import org.apache.asterix.optimizer.rules.util.TimeTravelUtil;
import org.apache.commons.lang3.mutable.Mutable;
import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
@@ -118,6 +119,14 @@ public class DatasetRewriter implements IFunctionToDataSourceRewriter, IResultTy
} else {
dataSource = metadataProvider.findDataSource(dsid);
}
+ // The time travel value must have folded to a constant by now: ConstantFoldingRule sits earlier in the
+ // same normalization rule collection as UnnestToDataScanRule, which is what got us here. This reads it
+ // unconditionally, including for dataset kinds that have no snapshots, so that a value which did not
+ // fold -- or folded to something unusable -- is reported rather than silently dropped.
+ TimeTravel timeTravel = unnest.getTimeTravel();
+ String timeTravelValue =
+ timeTravel == null ? null : TimeTravelUtil.resolve(timeTravel, unnest.getSourceLocation());
+
boolean hasMeta = dataSource.hasMeta();
if (hasMeta) {
variables.add(context.newVar());
@@ -132,12 +141,11 @@ public class DatasetRewriter implements IFunctionToDataSourceRewriter, IResultTy
if (externalSubpath instanceof String) {
dataSourceProperties.put(SUBPATH, (String) externalSubpath);
}
- Object timeTravelObj = unnest.getTimeTravel();
- if (timeTravelObj instanceof TimeTravel timeTravel) {
- if (timeTravel.getType().equals(TimeTravel.Type.SNAPSHOT_ID)) {
- dataSourceProperties.put(ICEBERG_SNAPSHOT_ID_PROPERTY_KEY, timeTravel.getSnapshotIdOrTimestamp());
- } else if (timeTravel.getType().equals(TimeTravel.Type.SNAPSHOT_TIMESTAMP)) {
- dataSourceProperties.put(ICEBERG_SNAPSHOT_TIMESTAMP_PROPERTY_KEY, timeTravel.getSnapshotIdOrTimestamp());
+ if (timeTravel != null) {
+ if (timeTravel.getType() == TimeTravel.Type.SNAPSHOT_ID) {
+ dataSourceProperties.put(ICEBERG_SNAPSHOT_ID_PROPERTY_KEY, timeTravelValue);
+ } else if (timeTravel.getType() == TimeTravel.Type.SNAPSHOT_TIMESTAMP) {
+ dataSourceProperties.put(ICEBERG_SNAPSHOT_TIMESTAMP_PROPERTY_KEY, timeTravelValue);
} else {
throw new IllegalStateException("Unknown snapshot type");
}
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/optimizer/rules/util/TimeTravelUtilTest.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/optimizer/rules/util/TimeTravelUtilTest.java
new file mode 100644
index 0000000000..39b17cd4ea
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/optimizer/rules/util/TimeTravelUtilTest.java
@@ -0,0 +1,315 @@
+/*
+ * 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.asterix.optimizer.rules.util;
+
+import static org.apache.asterix.external.util.iceberg.IcebergConstants.ICEBERG_SNAPSHOT_TIMESTAMP_PROPERTY_KEY;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.zone.ZoneRules;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.external.util.iceberg.IcebergSnapshotUtils;
+import org.apache.asterix.lang.common.util.FunctionUtil;
+import org.apache.asterix.om.base.ABoolean;
+import org.apache.asterix.om.base.ADate;
+import org.apache.asterix.om.base.ADateTime;
+import org.apache.asterix.om.base.ADouble;
+import org.apache.asterix.om.base.AInt16;
+import org.apache.asterix.om.base.AInt32;
+import org.apache.asterix.om.base.AInt64;
+import org.apache.asterix.om.base.AInt8;
+import org.apache.asterix.om.base.AString;
+import org.apache.asterix.om.base.ATime;
+import org.apache.asterix.om.base.IAObject;
+import org.apache.asterix.om.constants.AsterixConstantValue;
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.optimizer.rules.ResolveTimeTravelValueRule;
+import org.apache.asterix.runtime.evaluators.functions.temporal.CurrentDateTimeImmediateDescriptor;
+import org.apache.asterix.runtime.evaluators.functions.temporal.CurrentTemporalValueUtil;
+import org.apache.commons.lang3.mutable.MutableObject;
+import org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+import org.apache.hyracks.api.application.IServiceContext;
+import org.apache.hyracks.api.context.IEvaluatorContext;
+import org.apache.hyracks.api.context.IHyracksTaskContext;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.api.exceptions.IWarningCollector;
+import org.apache.hyracks.data.std.api.IPointable;
+import org.apache.hyracks.data.std.primitive.IntegerPointable;
+import org.apache.hyracks.data.std.primitive.LongPointable;
+import org.apache.hyracks.data.std.primitive.VoidPointable;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Covers the conversion of a folded {@code AT SNAPSHOT} / {@code AT TIMESTAMP} value into the string form the
+ * Iceberg reader parses, and the rejection of values it could not make sense of.
+ *
+ * @see org.apache.asterix.external.util.iceberg.IcebergSnapshotUtils
+ */
+public class TimeTravelUtilTest {
+
+ @Test
+ public void testSnapshotId() throws Exception {
+ Assert.assertEquals("8574821", snapshotId(new AInt64(8574821L)));
+ Assert.assertEquals("42", snapshotId(new AInt32(42)));
+ // a string passes straight through, which is what the original literal-only syntax produced
+ Assert.assertEquals("8574821", snapshotId(new AString("8574821")));
+ }
+
+ @Test
+ public void testTimestampEpochMillis() throws Exception {
+ Assert.assertEquals("1755648000000", timestamp(new AInt64(1755648000000L)));
+ }
+
+ @Test
+ public void testTimestampDate() throws Exception {
+ int epochDay = (int) LocalDate.of(2026, 8, 20).toEpochDay();
+ Assert.assertEquals("2026-08-20", timestamp(new ADate(epochDay)));
+ }
+
+ @Test
+ public void testTimestampDateTimeAlwaysHasSeconds() throws Exception {
+ long midnight = LocalDateTime.of(2026, 8, 20, 0, 0, 0).toInstant(ZoneOffset.UTC).toEpochMilli();
+ Assert.assertEquals("2026-08-20T00:00:00", timestamp(new ADateTime(midnight)));
+ }
+
+ @Test
+ public void testTimestampDateTimeKeepsMillis() throws Exception {
+ long instant = LocalDateTime.of(2026, 8, 20, 13, 45, 30, 123_000_000).toInstant(ZoneOffset.UTC).toEpochMilli();
+ Assert.assertEquals("2026-08-20T13:45:30.123", timestamp(new ADateTime(instant)));
+ }
+
+ /**
+ * A snapshot id is an integer; a date there is a mistake worth naming rather than passing on to fail as an
+ * unparseable number further down.
+ */
+ @Test
+ public void testDateIsRejectedForSnapshotId() {
+ assertRejected(TimeTravel.Type.SNAPSHOT_ID, new ADate((int) LocalDate.of(2026, 8, 20).toEpochDay()));
+ assertRejected(TimeTravel.Type.SNAPSHOT_ID, new ADateTime(0L));
+ }
+
+ @Test
+ public void testUnusableTypesAreRejected() {
+ for (TimeTravel.Type type : TimeTravel.Type.values()) {
+ assertRejected(type, new ADouble(1.5));
+ assertRejected(type, ABoolean.TRUE);
+ // a bare time of day cannot identify a snapshot
+ assertRejected(type, new ATime(0));
+ }
+ }
+
+ /**
+ * The value normally arrives already folded; anything still holding a function call means folding could not
+ * reduce it, and the scan has nothing usable.
+ */
+ @Test
+ public void testUnfoldedValueIsRejected() {
+ ScalarFunctionCallExpression notConstant =
+ new ScalarFunctionCallExpression(FunctionUtil.getFunctionInfo(BuiltinFunctions.CURRENT_DATE));
+ TimeTravel timeTravel = new TimeTravel(new MutableObject<>(notConstant), TimeTravel.Type.SNAPSHOT_TIMESTAMP);
+ try {
+ TimeTravelUtil.resolve(timeTravel, null);
+ Assert.fail("expected an unfolded value to be rejected");
+ } catch (CompilationException e) {
+ Assert.assertEquals(ErrorCode.EXPECTED_CONSTANT_VALUE.intValue(), e.getErrorCode());
+ }
+ }
+
+ @Test
+ public void testFoldedValueIsRead() throws Exception {
+ TimeTravel timeTravel = new TimeTravel(
+ new MutableObject<>(new ConstantExpression(new AsterixConstantValue(new AInt64(8574821L)))),
+ TimeTravel.Type.SNAPSHOT_ID);
+ Assert.assertEquals("8574821", TimeTravelUtil.resolve(timeTravel, null));
+ }
+
+ @Test
+ public void testSmallIntegerWidths() throws Exception {
+ Assert.assertEquals("7", snapshotId(new AInt8((byte) 7)));
+ Assert.assertEquals("300", snapshotId(new AInt16((short) 300)));
+ }
+
+ /**
+ * Snapshot ids are longs and Iceberg does not promise them positive, so the sign has to
+ * survive rather than being formatted away.
+ */
+ @Test
+ public void testNegativeAndExtremeSnapshotIds() throws Exception {
+ Assert.assertEquals("-8574821", snapshotId(new AInt64(-8574821L)));
+ Assert.assertEquals(String.valueOf(Long.MAX_VALUE), snapshotId(new AInt64(Long.MAX_VALUE)));
+ Assert.assertEquals(String.valueOf(Long.MIN_VALUE), snapshotId(new AInt64(Long.MIN_VALUE)));
+ }
+
+ @Test
+ public void testEpochBoundaries() throws Exception {
+ Assert.assertEquals("1970-01-01", timestamp(new ADate(0)));
+ Assert.assertEquals("1970-01-01T00:00:00", timestamp(new ADateTime(0L)));
+ // before the epoch: the date must go backwards, not wrap
+ Assert.assertEquals("1969-12-31", timestamp(new ADate(-1)));
+ }
+
+ @Test
+ public void testEmptyStringPassesThroughForTheReaderToReject() throws Exception {
+ // not this layer's job to validate the string's shape -- IcebergSnapshotUtils parses it
+ Assert.assertEquals("", snapshotId(new AString("")));
+ }
+
+ /**
+ * A clock in a time travel value is read while the query is compiled, and the reader takes the rendered
+ * value as UTC. Run in a zone behind UTC so a clock taken in the controller's own zone would select an
+ * instant hours in the past.
+ */
+ @Test
+ public void testClockValueSelectsTheCurrentInstant() throws Exception {
+ TimeZone saved = TimeZone.getDefault();
+ try {
+ TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
+ long now = LocalDateTime.of(2026, 8, 20, 23, 45, 30, 123_000_000).toInstant(ZoneOffset.UTC).toEpochMilli();
+ IAObject clock =
+ ResolveTimeTravelValueRule.wallClockInUtc(BuiltinFunctions.CURRENT_DATETIME_IMMEDIATE, now);
+ Assert.assertEquals(now, readerInstant(timestamp(clock)));
+
+ // half an hour later it is the 21st in UTC but still the 20th in Los Angeles: the date follows UTC
+ IAObject today = ResolveTimeTravelValueRule.wallClockInUtc(BuiltinFunctions.CURRENT_DATE_IMMEDIATE, now);
+ Assert.assertEquals("2026-08-20", timestamp(today));
+ IAObject tomorrow = ResolveTimeTravelValueRule.wallClockInUtc(BuiltinFunctions.CURRENT_DATE_IMMEDIATE,
+ now + TimeUnit.MINUTES.toMillis(30));
+ Assert.assertEquals("2026-08-21", timestamp(tomorrow));
+
+ IAObject time = ResolveTimeTravelValueRule.wallClockInUtc(BuiltinFunctions.CURRENT_TIME_IMMEDIATE, now);
+ Assert.assertEquals(new ATime((int) TimeUnit.HOURS.toMillis(23) + (int) TimeUnit.MINUTES.toMillis(45)
+ + (int) TimeUnit.SECONDS.toMillis(30) + 123), time);
+
+ Assert.assertNull(ResolveTimeTravelValueRule.wallClockInUtc(BuiltinFunctions.CURRENT_DATETIME, now));
+ } finally {
+ TimeZone.setDefault(saved);
+ }
+ }
+
+ /**
+ * The evaluators read the same instant in the job's zone through the same method; behind UTC the local day
+ * and time of day trail UTC's.
+ */
+ @Test
+ public void testCurrentValueInAZoneBehindUtc() {
+ ZoneRules la = ZoneId.of("America/Los_Angeles").getRules();
+ long now = LocalDateTime.of(2026, 8, 21, 0, 15, 30, 123_000_000).toInstant(ZoneOffset.UTC).toEpochMilli();
+ Assert.assertEquals(LocalDate.of(2026, 8, 20).toEpochDay(),
+ CurrentTemporalValueUtil.valueAt(ATypeTag.DATE, now, la));
+ Assert.assertEquals(
+ TimeUnit.HOURS.toMillis(17) + TimeUnit.MINUTES.toMillis(15) + TimeUnit.SECONDS.toMillis(30) + 123,
+ CurrentTemporalValueUtil.valueAt(ATypeTag.TIME, now, la));
+ Assert.assertEquals(now - TimeUnit.HOURS.toMillis(7),
+ CurrentTemporalValueUtil.valueAt(ATypeTag.DATETIME, now, la));
+ }
+
+ /**
+ * A clock that renders a zone writes an offset, "Z" or "+03:00"; the reader must take the instant it names.
+ */
+ @Test
+ public void testReaderTakesAnOffsetTimestampAsTheInstantItNames() throws Exception {
+ long instant = LocalDateTime.of(2026, 8, 20, 13, 45, 30, 123_000_000).toInstant(ZoneOffset.UTC).toEpochMilli();
+ Assert.assertEquals(instant, readerInstant("2026-08-20T13:45:30.123Z"));
+ Assert.assertEquals(instant, readerInstant("2026-08-20T16:45:30.123+03:00"));
+ Assert.assertEquals(instant, readerInstant("2026-08-20T06:45:30.123-07:00"));
+ Assert.assertEquals(instant, readerInstant("2026-08-20T13:45:30.123"));
+ }
+
+ /**
+ * Only the time travel value reads the clock in UTC, and it does so without running the evaluator: the
+ * evaluator itself still has no zone to read the clock in without a job, and keeps failing.
+ */
+ @Test
+ public void testClockEvaluatorWithoutJobFails() {
+ IEvaluatorContext noJob = new NoJobContext();
+ HyracksDataException e = Assert.assertThrows(HyracksDataException.class,
+ () -> evaluate(CurrentDateTimeImmediateDescriptor.FACTORY, noJob));
+ Assert.assertEquals(org.apache.hyracks.api.exceptions.ErrorCode.ILLEGAL_STATE.intValue(), e.getErrorCode());
+ Assert.assertTrue(e.getMessage(), e.getMessage().contains("job-start-timezone"));
+ }
+
+ private static IAObject evaluate(IFunctionDescriptorFactory factory, IEvaluatorContext ctx) throws Exception {
+ IPointable result = new VoidPointable();
+ factory.createFunctionDescriptor().createEvaluatorFactory(new IScalarEvaluatorFactory[0])
+ .createScalarEvaluator(ctx).evaluate(null, result);
+ byte[] bytes = result.getByteArray();
+ int start = result.getStartOffset();
+ ATypeTag tag = ATypeTag.VALUE_TYPE_MAPPING[bytes[start]];
+ switch (tag) {
+ case DATETIME:
+ return new ADateTime(LongPointable.getLong(bytes, start + 1));
+ case DATE:
+ return new ADate(IntegerPointable.getInteger(bytes, start + 1));
+ default:
+ throw new IllegalStateException("unexpected clock type " + tag);
+ }
+ }
+
+ private static long readerInstant(String rendered) throws CompilationException {
+ return IcebergSnapshotUtils.validateAndGetSnapshot(Map.of(ICEBERG_SNAPSHOT_TIMESTAMP_PROPERTY_KEY, rendered))
+ .orElseThrow();
+ }
+
+ private static String snapshotId(IAObject value) throws CompilationException {
+ return TimeTravelUtil.stringify(value, TimeTravel.Type.SNAPSHOT_ID, null);
+ }
+
+ private static String timestamp(IAObject value) throws CompilationException {
+ return TimeTravelUtil.stringify(value, TimeTravel.Type.SNAPSHOT_TIMESTAMP, null);
+ }
+
+ private static void assertRejected(TimeTravel.Type type, IAObject value) {
+ try {
+ String resolved = TimeTravelUtil.stringify(value, type, null);
+ Assert.fail("expected " + value.getType().getTypeTag() + " to be rejected, got " + resolved);
+ } catch (CompilationException e) {
+ Assert.assertEquals(ErrorCode.INVALID_TIME_TRAVEL_VALUE.intValue(), e.getErrorCode());
+ }
+ }
+
+ private static class NoJobContext implements IEvaluatorContext {
+ @Override
+ public IServiceContext getServiceContext() {
+ return null;
+ }
+
+ @Override
+ public IHyracksTaskContext getTaskContext() {
+ return null;
+ }
+
+ @Override
+ public IWarningCollector getWarningCollector() {
+ return null;
+ }
+ }
+}
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/time-travel/time-travel-clock-value.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/time-travel/time-travel-clock-value.sqlpp
new file mode 100644
index 0000000000..86844ee895
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/time-travel/time-travel-clock-value.sqlpp
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/*
+ * Description : A time travel value may be built from a clock function. Such a function is impure and is not
+ * : folded anywhere else, but this position is consumed before the job exists, so it is resolved
+ * : here. The datasets are internal, so the resolved value is discarded and the plan is the plain
+ * : plan -- what is tested is that each form compiles, which it cannot do unless the value was
+ * : reduced to a constant.
+ * Expected Res : Success
+ */
+
+drop dataverse timetravel if exists;
+create dataverse timetravel;
+
+use timetravel;
+
+create type timetravel.UserType as {
+ uid : integer
+};
+
+create dataset Users(UserType) primary key uid;
+
+select element {'a': u.uid, 'b': v.uid, 'c': w.uid}
+from Users as u AT TIMESTAMP current_datetime_immediate() - duration("P3M"),
+ Users as v AT TIMESTAMP current_date_immediate(),
+ Users as w AT SNAPSHOT unix_time_from_datetime_in_ms(current_datetime_immediate())
+where u.uid = v.uid and v.uid = w.uid
+;
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/time-travel/time-travel-expression.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/time-travel/time-travel-expression.sqlpp
new file mode 100644
index 0000000000..f17d68d06d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/time-travel/time-travel-expression.sqlpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+/*
+ * Description : AT SNAPSHOT / AT TIMESTAMP accept any expression that folds to a constant, which is resolved
+ * : during expression-to-plan translation. This exercises that resolution: the datasets here are
+ * : internal, so the resolved value is discarded and the plan is the plain plan -- what is being
+ * : tested is that each of these forms compiles at all.
+ * Expected Res : Success
+ */
+
+drop dataverse timetravel if exists;
+create dataverse timetravel;
+
+use timetravel;
+
+create type timetravel.UserType as {
+ uid : integer
+};
+
+create dataset Users(UserType) primary key uid;
+
+create dataset Visitors(UserType) primary key uid;
+
+-- param $ts:string=8574821
+
+select element {'a': u.uid, 'b': v.uid, 'c': w.uid, 'd': x.uid, 'e': y.uid, 'f': z.uid}
+from Users as u AT SNAPSHOT 8574821,
+ Users as v AT TIMESTAMP date("2026-08-20") - duration("P2D")
+ join Visitors as w AT TIMESTAMP unix_time_from_date_in_ms(date("2026-08-20")) on v.uid = w.uid,
+ -- a year-month duration, and one off a datetime carrying a millisecond fraction
+ Visitors as x AT TIMESTAMP date("2026-08-20") - duration("P3M"),
+ Users as y AT TIMESTAMP datetime("2026-08-20T13:45:30.123") - duration("P3M"),
+ Users as z AT SNAPSHOT $ts
+where u.uid = v.uid and w.uid = x.uid and x.uid = y.uid and y.uid = z.uid
+;
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/time-travel/time-travel-clock-value.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/time-travel/time-travel-clock-value.plan
new file mode 100644
index 0000000000..0bdda61a01
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/time-travel/time-travel-clock-value.plan
@@ -0,0 +1,70 @@
+distribute result [$$60]
+-- DISTRIBUTE_RESULT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$60] <- [{"a": $$64, "b": $$65, "c": $$66}] project: [$$60]
+ -- ASSIGN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ join (eq($$65, $$66))
+ -- HYBRID_HASH_JOIN [$$65][$$66] |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ join (eq($$64, $$65))
+ -- HYBRID_HASH_JOIN [$$64][$$65] |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$64] <- [$$65] project: [$$64]
+ -- ASSIGN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate
+ -- REPLICATE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$65])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$65, $$v] <- timetravel.Users
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate
+ -- REPLICATE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$65])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$65, $$v] <- timetravel.Users
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$66] <- [$$65] project: [$$66]
+ -- ASSIGN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate
+ -- REPLICATE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$65])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$65, $$v] <- timetravel.Users
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/time-travel/time-travel-expression.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/time-travel/time-travel-expression.plan
new file mode 100644
index 0000000000..7e6bee7702
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/time-travel/time-travel-expression.plan
@@ -0,0 +1,138 @@
+distribute result [$$119]
+-- DISTRIBUTE_RESULT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$119] <- [{"a": $$126, "b": $$127, "c": $$128, "d": $$129, "e": $$130, "f": $$131}] project: [$$119]
+ -- ASSIGN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ join (eq($$130, $$131))
+ -- HYBRID_HASH_JOIN [$$130][$$131] |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ join (eq($$129, $$130))
+ -- HYBRID_HASH_JOIN [$$129][$$130] |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ join (eq($$128, $$129))
+ -- HYBRID_HASH_JOIN [$$128][$$129] |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ join (eq($$126, $$127))
+ -- HYBRID_HASH_JOIN [$$126][$$127] |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$126] <- [$$127] project: [$$126]
+ -- ASSIGN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate
+ -- REPLICATE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$127])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$127, $$v] <- timetravel.Users
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ join (eq($$127, $$128))
+ -- HYBRID_HASH_JOIN [$$127][$$128] |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate
+ -- REPLICATE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$127])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$127, $$v] <- timetravel.Users
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate
+ -- REPLICATE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$128])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$128, $$w] <- timetravel.Visitors
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$129] <- [$$128] project: [$$129]
+ -- ASSIGN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate
+ -- REPLICATE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$128])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$128, $$w] <- timetravel.Visitors
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$130] <- [$$127] project: [$$130]
+ -- ASSIGN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate
+ -- REPLICATE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$127])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$127, $$v] <- timetravel.Users
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$131] <- [$$127] project: [$$131]
+ -- ASSIGN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate
+ -- REPLICATE |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$127])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$127, $$v] <- timetravel.Users
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/time-travel/time-travel-clock-value.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/time-travel/time-travel-clock-value.plan
new file mode 100644
index 0000000000..ad936eee6d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/time-travel/time-travel-clock-value.plan
@@ -0,0 +1,70 @@
+distribute result [$$60] [cardinality: 1000000.0, doc-size: -3.0, op-cost: 0.0, total-cost: 3999977.11]
+-- DISTRIBUTE_RESULT |PARTITIONED|
+ exchange [cardinality: 1000000.0, doc-size: -3.0, op-cost: 0.0, total-cost: 3999977.11]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$60] <- [{"a": $$64, "b": $$65, "c": $$66}] project: [$$60] [cardinality: 1000000.0, doc-size: -3.0, op-cost: 0.0, total-cost: 3999977.11]
+ -- ASSIGN |PARTITIONED|
+ exchange [cardinality: 1000000.0, doc-size: -3.0, op-cost: 0.0, total-cost: 3999977.11]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ join (eq($$65, $$66)) [cardinality: 1000000.0, doc-size: -3.0, op-cost: 750000.0, total-cost: 3999977.11]
+ -- HYBRID_HASH_JOIN [$$65][$$66] |PARTITIONED|
+ exchange [cardinality: 1000000.0, doc-size: -2.0, op-cost: 0.0, total-cost: 2249984.74]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ join (eq($$64, $$65)) [cardinality: 1000000.0, doc-size: -2.0, op-cost: 750000.0, total-cost: 2249984.74]
+ -- HYBRID_HASH_JOIN [$$64][$$65] |PARTITIONED|
+ exchange [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$64] <- [$$65] project: [$$64] [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- ASSIGN |PARTITIONED|
+ exchange [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- REPLICATE |PARTITIONED|
+ exchange [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$65]) [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$65, $$v] <- timetravel.Users [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ exchange [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- REPLICATE |PARTITIONED|
+ exchange [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$65]) [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$65, $$v] <- timetravel.Users [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ exchange [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$66] <- [$$65] project: [$$66] [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- ASSIGN |PARTITIONED|
+ exchange [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- REPLICATE |PARTITIONED|
+ exchange [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$65]) [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$65, $$v] <- timetravel.Users [cardinality: 1000000.0, doc-size: -1.0, op-cost: 0.0, total-cost: 499992.37]
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/time-travel/time-travel-expression.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/time-travel/time-travel-expression.plan
new file mode 100644
index 0000000000..eff3d76b22
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results_cbo/time-travel/time-travel-expression.plan
@@ -0,0 +1,112 @@
+distribute result [$$119] [cardinality: 9.223372036854776E16, doc-size: -6.0, op-cost: 0.0, total-cost: 9.223372036854776E16]
+-- DISTRIBUTE_RESULT |PARTITIONED|
+ exchange [cardinality: 9.223372036854776E16, doc-size: -6.0, op-cost: 0.0, total-cost: 9.223372036854776E16]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$119] <- [{"a": $$126, "b": $$127, "c": $$128, "d": $$129, "e": $$130, "f": $$131}] project: [$$119] [cardinality: 9.223372036854776E16, doc-size: -6.0, op-cost: 0.0, total-cost: 9.223372036854776E16]
+ -- ASSIGN |PARTITIONED|
+ project ([$$126, $$127, $$128, $$129, $$130, $$131]) [cardinality: 9.223372036854776E16, doc-size: -6.0, op-cost: 0.0, total-cost: 9.223372036854776E16]
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange [cardinality: 9.223372036854776E16, doc-size: -6.0, op-cost: 0.0, total-cost: 9.223372036854776E16]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ unnest-map [$$131, $$z] <- index-search("Users", 0, "Default", "timetravel", "Users", true, true, 1, $$130, 1, $$130, true, true, true) [cardinality: 9.223372036854776E16, doc-size: -6.0, op-cost: 0.0, total-cost: 9.223372036854776E16]
+ -- BTREE_SEARCH |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ order (ASC, $$130) [cardinality: 9.223372036854776E16, doc-size: -6.0, op-cost: 9.223372036854776E16, total-cost: 9.223372036854776E16]
+ -- STABLE_SORT [$$130(ASC)] |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$126, $$127, $$128, $$129, $$130]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ unnest-map [$$130, $$y] <- index-search("Users", 0, "Default", "timetravel", "Users", true, true, 1, $$129, 1, $$129, true, true, true) [cardinality: 9.223372036854776E16, doc-size: -5.0, op-cost: 9.223372036854776E16, total-cost: 9.223372036854776E16]
+ -- BTREE_SEARCH |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ order (ASC, $$129) [cardinality: 9.223372036854776E16, doc-size: -5.0, op-cost: 9.223372036854776E16, total-cost: 9.223372036854776E16]
+ -- STABLE_SORT [$$129(ASC)] |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ join (eq($$128, $$129)) [cardinality: 1.0E12, doc-size: -4.0, op-cost: 2.500005E11, total-cost: 2.5000549996948E11]
+ -- HYBRID_HASH_JOIN [$$128][$$129] |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ join (eq($$127, $$128)) [cardinality: 1000000.0, doc-size: -3.0, op-cost: 750000.0, total-cost: 3999977.11]
+ -- HYBRID_HASH_JOIN [$$127][$$128] |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ join (eq($$126, $$127)) [cardinality: 1000000.0, doc-size: -2.0, op-cost: 750000.0, total-cost: 2249984.74]
+ -- HYBRID_HASH_JOIN [$$126][$$127] |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$126] <- [$$127] project: [$$126] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ASSIGN |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- REPLICATE |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$127]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$127, $$v] <- timetravel.Users [cardinality: 1000000.0, doc-size: -1.0, op-cost: 499992.37, total-cost: 499992.37]
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- REPLICATE |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$127]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$127, $$v] <- timetravel.Users [cardinality: 1000000.0, doc-size: -1.0, op-cost: 499992.37, total-cost: 499992.37]
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- REPLICATE |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$128]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$128, $$w] <- timetravel.Visitors [cardinality: 1000000.0, doc-size: -1.0, op-cost: 499992.37, total-cost: 499992.37]
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$129] <- [$$128] project: [$$129] [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ASSIGN |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ replicate [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- REPLICATE |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$128]) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$128, $$w] <- timetravel.Visitors [cardinality: 1000000.0, doc-size: -1.0, op-cost: 499992.37, total-cost: 499992.37]
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/negative/bad-snapshot-format/test.090.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/negative/bad-snapshot-format/test.090.query.sqlpp
index 552c9f4af7..7f5073d086 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/negative/bad-snapshot-format/test.090.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a stringified uuid is a string, so it passes the value type check; it is rejected where the snapshot id
+// is parsed, since it is not a number
+SELECT count(*) AS count
+FROM goodSnapshot AT snapshot to_string(uuid());
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/negative/bad-snapshot-format/test.100.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/negative/bad-snapshot-format/test.100.query.sqlpp
index 552c9f4af7..aebc829054 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/negative/bad-snapshot-format/test.100.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// the same string as a timestamp: it is none of the accepted timestamp forms
+SELECT count(*) AS count
+FROM goodSnapshot AT timestamp to_string(uuid());
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/negative/bad-snapshot-format/test.110.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/negative/bad-snapshot-format/test.110.query.sqlpp
index 552c9f4af7..5086ed0426 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/negative/bad-snapshot-format/test.110.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a clock value ten years back: the value reaches snapshot selection, and no snapshot is that old
+SELECT count(*) AS count
+FROM goodSnapshot AT timestamp current_datetime_immediate() - duration("P10Y");
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.085.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.085.query.sqlpp
index 552c9f4af7..f1d8d61710 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.085.query.sqlpp
@@ -17,20 +17,8 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a bare integer snapshot id: the value is an expression now, not only a string literal.
+// snapshot 1 holds ids 1-800, so pointing this latest-reading collection at it gives 800
+// rather than the 997 it returns on its own
+SELECT count(*) as count
+FROM usersNoSnapshot AT snapshot %SNAPSHOT_1_SNAPSHOT_ID%;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.090.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.090.query.sqlpp
index 552c9f4af7..1e2c581794 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.090.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a bare integer timestamp (epoch millis). The captured instant is just after snapshot 1 was
+// committed, so it resolves to snapshot 1: ids 1-800 => 800, not the latest 997
+SELECT count(*) as count
+FROM usersNoSnapshot AT timestamp %SNAPSHOT_1_TIMESTAMP_LONG_PLACEHOLDER%;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.100.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.100.query.sqlpp
index 552c9f4af7..a9e9901d2d 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.100.query.sqlpp
@@ -17,20 +17,8 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a datetime constructor call, folded at compile time. The instant is just after snapshot 2,
+// which holds ids 1-1000 => 1000. This collection's own snapshotId pins it to snapshot 1 (800),
+// so the statement's value has to win for the answer to be 1000
+SELECT count(*) as count
+FROM usersYesSnapshot AT timestamp datetime("%SNAPSHOT_2_TIMESTAMP_DATETIME_PLACEHOLDER%");
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.110.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.110.query.sqlpp
index 552c9f4af7..fc2f3a8fba 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.110.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// the same instant converted to epoch millis by a folded function call, so it must still land on
+// snapshot 2: ids 1-1000 => 1000, again overriding the collection's own snapshot 1 (800)
+SELECT count(*) as count
+FROM usersYesSnapshot AT timestamp unix_time_from_datetime_in_ms(datetime("%SNAPSHOT_2_TIMESTAMP_DATETIME_PLACEHOLDER%"));
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.120.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.120.query.sqlpp
index 552c9f4af7..ed976d913e 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.120.query.sqlpp
@@ -17,20 +17,8 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// date arithmetic: the date is the day after the last commit, and subtracting a zero duration
+// must not shift it, so this resolves to the latest snapshot -- ids 1-1000 less the deleted ids
+// 10/20/30 => 997, overriding the collection's own snapshot 1 (800)
+SELECT count(*) as count
+FROM usersYesSnapshot AT timestamp date("%SNAPSHOT_3_TIMESTAMP_DATE_PLACEHOLDER%") - duration("P0D");
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.140.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.140.query.sqlpp
index 552c9f4af7..c0e3c0d84a 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.140.query.sqlpp
@@ -17,20 +17,9 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// the same collection at two snapshots and at latest, in one statement: each subquery resolves
+// its own value. snapshot 1 is ids 1-800 (800), snapshot 2 is ids 1-1000 (1000), and latest is
+// ids 1-1000 less the deleted ids 10/20/30 (997)
+SELECT (SELECT VALUE count(*) FROM usersNoSnapshot AT snapshot %SNAPSHOT_1_SNAPSHOT_ID%)[0] AS s1,
+ (SELECT VALUE count(*) FROM usersNoSnapshot AT timestamp "%SNAPSHOT_2_TIMESTAMP_DATETIME_PLACEHOLDER%")[0] AS s2,
+ (SELECT VALUE count(*) FROM usersNoSnapshot)[0] AS latest;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.150.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.150.query.sqlpp
index 552c9f4af7..abc9faadc4 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.150.query.sqlpp
@@ -17,20 +17,9 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a JOIN clause carries its own snapshot. The left side reads latest (ids 1-1000 less the
+// deleted ids 10/20/30) and the right side snapshot 1 (ids 1-800), so the ids present in both
+// are 1-800 less 10/20/30 => 797
+SELECT count(*) AS count
+FROM usersNoSnapshot AS a
+JOIN usersNoSnapshot AS b AT snapshot %SNAPSHOT_1_SNAPSHOT_ID% ON a.id = b.id;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.160.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.160.query.sqlpp
index 552c9f4af7..4bf5bc37d5 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.160.query.sqlpp
@@ -17,20 +17,9 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// the snapshot survives the RIGHT OUTER JOIN rewrite, which swaps the join inputs. A right outer
+// join keeps every row of the snapshotted right side, so the count is snapshot 1's own
+// cardinality -- ids 1-800 => 800. The left side's missing 10/20/30 only null out those rows
+SELECT count(*) AS count
+FROM usersNoSnapshot AS a
+RIGHT OUTER JOIN usersNoSnapshot AS b AT snapshot %SNAPSHOT_1_SNAPSHOT_ID% ON a.id = b.id;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.170.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.170.query.sqlpp
index 552c9f4af7..d0cde59d16 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.170.query.sqlpp
@@ -17,20 +17,9 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+-- param $ts:string=2030-01-01
+// the value supplied as a query parameter. 2030-01-01 is past every snapshot, so it resolves to
+// the latest one: ids 1-1000 less the deleted ids 10/20/30 => 997. A generated snapshot id
+// cannot be used here because the harness extracts parameters before it substitutes the
+// snapshot placeholders
+SELECT count(*) AS count FROM usersNoSnapshot AT timestamp $ts;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.180.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.180.query.sqlpp
index 552c9f4af7..93262f0358 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.180.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a value bound earlier in the statement by WITH. It still has to fold to snapshot 1's id, so
+// this reads snapshot 1 -- ids 1-800 => 800, not the latest 997
+WITH s AS %SNAPSHOT_1_SNAPSHOT_ID%
+SELECT count(*) AS count FROM usersNoSnapshot AT snapshot s;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.190.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.190.query.sqlpp
index 552c9f4af7..5341db1a7e 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.190.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// arithmetic on the snapshot id itself: adding zero must fold back to the same id, so this stays
+// on snapshot 1 -- ids 1-800 => 800 rather than the latest 997
+SELECT count(*) AS count FROM usersNoSnapshot AT snapshot %SNAPSHOT_1_SNAPSHOT_ID% + 0;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.200.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.200.query.sqlpp
index 552c9f4af7..e9a7d8378a 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.200.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a time travelling scan inside a subquery: the value belongs to the inner FROM term, so the
+// outer count still sees only snapshot 1's ids 1-800 => 800, not the latest 997
+SELECT count(*) AS count
+FROM (SELECT VALUE u FROM usersNoSnapshot AS u AT snapshot %SNAPSHOT_1_SNAPSHOT_ID%) AS t;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.210.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.210.ddl.sqlpp
index 552c9f4af7..ccd3b9a8f1 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.210.ddl.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+CREATE FUNCTION snapId() {
+ %SNAPSHOT_1_SNAPSHOT_ID%
+};
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.220.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.220.query.sqlpp
index 552c9f4af7..84ef5c07f1 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.220.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a user-defined function whose body is a constant, inlined into the value before folding. It
+// returns snapshot 1's id, so this reads ids 1-800 => 800 rather than the latest 997
+SELECT count(*) AS count FROM usersNoSnapshot AT snapshot snapId();
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.230.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.230.ddl.sqlpp
index 552c9f4af7..02af60383d 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.230.ddl.sqlpp
@@ -17,20 +17,4 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+DROP FUNCTION snapId@0;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.240.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.240.query.sqlpp
index 552c9f4af7..207cb4f3d7 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.240.query.sqlpp
@@ -17,20 +17,11 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a snapshotted JOIN that precedes a RIGHT OUTER JOIN: the rewrite rebuilds the preceding
+// clause, and its snapshot has to survive that rebuild. a and c read usersYesSnapshot, pinned to
+// snapshot 1 (ids 1-800), and b is aimed at snapshot 1 too, so all 800 ids match => 800. Had the
+// rebuild dropped b's snapshot, b would read latest, lose 10/20/30, and give 797
+SELECT count(b.id) AS matched
+FROM usersYesSnapshot AS a
+JOIN usersNoSnapshot AS b AT snapshot %SNAPSHOT_1_SNAPSHOT_ID% ON a.id = b.id
+RIGHT OUTER JOIN usersYesSnapshot AS c ON b.id = c.id;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.250.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.250.ddl.sqlpp
index 552c9f4af7..82cff1ab12 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.250.ddl.sqlpp
@@ -17,20 +17,8 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+CREATE FUNCTION joinedAtSnapshot() {
+ (SELECT count(b.id) AS matched
+ FROM usersYesSnapshot AS a
+ JOIN usersNoSnapshot AS b AT snapshot %SNAPSHOT_1_SNAPSHOT_ID% ON a.id = b.id)[0].matched
+};
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.260.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.260.query.sqlpp
index 552c9f4af7..d7e2eb09e5 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.260.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a snapshotted JOIN inside a user-defined function body: inlining the function clones the
+// clause, which is the path the AST cloners take. Same shape as test.240 without the outer right
+// outer join, so all 800 of snapshot 1's ids match => 800; a dropped snapshot would give 797
+SELECT joinedAtSnapshot() AS matched;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.270.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.270.ddl.sqlpp
index 552c9f4af7..e7f32f40e2 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.270.ddl.sqlpp
@@ -17,20 +17,4 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+DROP FUNCTION joinedAtSnapshot@0;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.280.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.280.ddl.sqlpp
index 552c9f4af7..db996c97be 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.280.ddl.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// the view body pins snapshot 1 (ids 1-800); test.290 counts through it and must see 800
+CREATE VIEW usersAtSnapshot AS
+SELECT VALUE u FROM usersNoSnapshot AS u AT snapshot %SNAPSHOT_1_SNAPSHOT_ID%;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.290.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.290.query.sqlpp
index 552c9f4af7..ca7634ee23 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.290.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a view over a time travelling scan. A view body is stored as source text and re-parsed on use,
+// so this exercises the grammar a second time rather than the AST that produced it. The view pins
+// snapshot 1, so counting through it gives ids 1-800 => 800, not the latest 997
+SELECT count(*) AS count FROM usersAtSnapshot;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.300.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.300.ddl.sqlpp
index 552c9f4af7..388772a1ad 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.300.ddl.sqlpp
@@ -17,20 +17,5 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// drop the view created by test.280 so the collections can be dropped at the end
+DROP VIEW usersAtSnapshot;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.310.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.310.query.sqlpp
index 552c9f4af7..eff3705f27 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.310.query.sqlpp
@@ -17,20 +17,13 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// GROUP BY above a snapshotted JOIN: the group-by sugar rewrite clones the FROM clause, so the
+// JOIN's snapshot has to survive that clone. The left side reads latest (ids 1-1000 less the
+// deleted ids 10/20/30) and the right side snapshot 1 (ids 1-800), so 797 ids match: 397 at or
+// below 400 (400 less the deleted 10/20/30) and 400 above it. Had the clone dropped the snapshot,
+// the right side would read latest too and the false group would be 600 instead of 400
+SELECT lo, count(*) AS c
+FROM usersNoSnapshot AS a
+JOIN usersNoSnapshot AS b AT snapshot %SNAPSHOT_1_SNAPSHOT_ID% ON a.id = b.id
+GROUP BY a.id <= 400 AS lo
+ORDER BY lo;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.320.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.320.query.sqlpp
index 552c9f4af7..08118004bd 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.320.query.sqlpp
@@ -17,20 +17,9 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a window function over a snapshotted scan: the window rewrite restructures the select block
+// above the FROM term. row_number() numbers the snapshot's own rows, so the highest is snapshot
+// 1's cardinality -- ids 1-800 => 800, where the latest snapshot would reach 997
+SELECT max(t.rn) AS maxRn
+FROM (SELECT row_number() OVER (ORDER BY u.id) AS rn
+ FROM usersNoSnapshot AS u AT snapshot %SNAPSHOT_1_SNAPSHOT_ID%) AS t;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.330.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.330.query.sqlpp
index 552c9f4af7..251e9911db 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.330.query.sqlpp
@@ -17,20 +17,10 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// UNION ALL of the same collection at a snapshot and at latest: each branch is its own FROM term,
+// built by the set-operation rewrite, so each keeps its own value. snapshot 1 contributes 800 rows
+// (ids 1-800) and latest contributes 997 (ids 1-1000 less the deleted ids 10/20/30) => 1797
+SELECT count(*) AS c
+FROM (SELECT u.id FROM usersNoSnapshot AS u AT snapshot %SNAPSHOT_1_SNAPSHOT_ID%
+ UNION ALL
+ SELECT v.id FROM usersNoSnapshot AS v) AS t;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.340.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.340.query.sqlpp
index 552c9f4af7..15c5a4ffea 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.340.query.sqlpp
@@ -17,20 +17,11 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// RIGHT OUTER JOIN with a snapshot on *both* sides: the rewrite swaps the join inputs, so both
+// values have to survive the swap. Counting the LEFT id is what discriminates -- the join keeps
+// all 800 right rows either way, but a.id is null wherever the left side lacks that id. Both
+// sides read snapshot 1 (ids 1-800), so every row matches => 800; had the left snapshot been
+// dropped, the left side would read latest, lose 10/20/30, and give 797
+SELECT count(a.id) AS matched
+FROM usersNoSnapshot AS a AT snapshot %SNAPSHOT_1_SNAPSHOT_ID%
+RIGHT OUTER JOIN usersNoSnapshot AS b AT snapshot %SNAPSHOT_1_SNAPSHOT_ID% ON a.id = b.id;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.360.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.360.query.sqlpp
index 552c9f4af7..87ba481749 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.360.query.sqlpp
@@ -17,20 +17,10 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// three FROM terms, a different value on each, so the result has to discriminate all three:
+// a reads snapshot 1 (ids 1-800), b snapshot 2 (ids 1-1000), and c latest (ids 1-1000 less the
+// deleted ids 10/20/30). The ids present in all three are 1-800 less 10/20/30 => 797
+SELECT count(*) AS c
+FROM usersNoSnapshot AS a AT snapshot %SNAPSHOT_1_SNAPSHOT_ID%
+JOIN usersNoSnapshot AS b AT timestamp "%SNAPSHOT_2_TIMESTAMP_DATETIME_PLACEHOLDER%" ON a.id = b.id
+JOIN usersNoSnapshot AS c ON b.id = c.id;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.370.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.370.query.sqlpp
index 552c9f4af7..d5a8bdd264 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.370.query.sqlpp
@@ -17,20 +17,8 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a clock value: the clock is read while the query is compiled, which is after every snapshot of the
+// fixture was committed, so the latest snapshot is read => 997 (ids 1-1000 less the deleted 10/20/30);
+// snapshot 1 would give 800 and snapshot 2 1000
+SELECT count(*) AS count
+FROM usersNoSnapshot AT timestamp current_datetime_immediate();
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.380.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.380.query.sqlpp
index 552c9f4af7..cb1867e96d 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.380.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// the same clock read, converted to epoch milliseconds, the other timestamp form => 997
+SELECT count(*) AS count
+FROM usersNoSnapshot AT timestamp unix_time_from_datetime_in_ms(current_datetime_immediate());
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.390.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.390.query.sqlpp
index 552c9f4af7..b38fd96c3a 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.390.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a clock date: the date is taken in UTC and read as midnight UTC, so a day is added to land after
+// today's snapshots => 997
+SELECT count(*) AS count
+FROM usersNoSnapshot AT timestamp current_date_immediate() + duration("P1D");
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.400.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.400.query.sqlpp
index 552c9f4af7..0484199c25 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.400.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a stringified snapshot id: the string is parsed back to the id => snapshot 1, 800
+SELECT count(*) AS count
+FROM usersNoSnapshot AT snapshot to_string(%SNAPSHOT_1_SNAPSHOT_ID%);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.410.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.410.query.sqlpp
index 552c9f4af7..9962a36e6a 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.410.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a stringified clock, as epoch milliseconds: the string is parsed back to the millisecond value => 997
+SELECT count(*) AS count
+FROM usersNoSnapshot AT timestamp to_string(unix_time_from_datetime_in_ms(current_datetime_immediate()));
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.420.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.420.query.sqlpp
index 552c9f4af7..226d8160f9 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.420.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a stringified clock, as a datetime: the string is parsed back in the yyyy-MM-dd'T'HH:mm:ss form => 997
+SELECT count(*) AS count
+FROM usersNoSnapshot AT timestamp to_string(current_datetime_immediate());
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.430.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.430.query.sqlpp
index 552c9f4af7..ad1160daa8 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.430.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a stringified timestamp that lands between snapshot 2 and the deletes: the string is parsed back to the
+// millisecond value and selects snapshot 2 => 1000 (ids 1-1000, before 10/20/30 were deleted)
+SELECT count(*) AS count
+FROM usersNoSnapshot AT timestamp to_string(unix_time_from_datetime_in_ms(datetime("%SNAPSHOT_2_TIMESTAMP_DATETIME_PLACEHOLDER%")));
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.440.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.440.query.sqlpp
new file mode 100644
index 0000000000..c5702d8a07
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.440.query.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+// a clock must name the current instant, not the wall time in the server's zone: T1 is a true epoch instant
+// taken after snapshot 1, so a correct clock is later than T1 by less than the window and the CASE picks T1,
+// which selects snapshot 1 => 800. A clock off by its zone's UTC offset falls outside the window and reads
+// the latest snapshot instead => 997
+SELECT count(*) AS count
+FROM usersNoSnapshot AT timestamp
+ CASE WHEN current_datetime_immediate() >= datetime_from_unix_time_in_ms(%SNAPSHOT_1_TIMESTAMP_LONG_PLACEHOLDER%)
+ AND current_datetime_immediate() < datetime_from_unix_time_in_ms(%SNAPSHOT_1_TIMESTAMP_LONG_PLACEHOLDER%)
+ + duration("PT2H")
+ THEN datetime_from_unix_time_in_ms(%SNAPSHOT_1_TIMESTAMP_LONG_PLACEHOLDER%)
+ ELSE current_datetime_immediate() END;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.450.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.450.query.sqlpp
index 552c9f4af7..12d7e7e0a2 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.450.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a datetime at millisecond precision names the same instant as the epoch value it came from: T1 as a
+// datetime selects snapshot 1, as test.050 does with T1 as milliseconds => 800
+SELECT count(*) AS count
+FROM usersNoSnapshot AT timestamp datetime_from_unix_time_in_ms(%SNAPSHOT_1_TIMESTAMP_LONG_PLACEHOLDER%);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.455.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.455.ddl.sqlpp
index 552c9f4af7..f06a1db674 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.455.ddl.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a function that takes its time travel value as a parameter
+CREATE FUNCTION countAt(ts) {
+ (SELECT VALUE count(*) FROM usersNoSnapshot AT timestamp ts)[0]
+};
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.460.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.460.query.sqlpp
index 552c9f4af7..70525429b2 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.460.query.sqlpp
@@ -17,20 +17,5 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// the argument is substituted into the value when the body is inlined, so it selects the snapshot: T1 => 800
+SELECT countAt(%SNAPSHOT_1_TIMESTAMP_LONG_PLACEHOLDER%) AS count;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.470.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.470.query.sqlpp
index 552c9f4af7..2fb1ff0cf8 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.470.query.sqlpp
@@ -17,20 +17,5 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a clock argument reads the clock while the query is compiled => the latest snapshot, 997
+SELECT countAt(current_datetime_immediate()) AS count;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.480.cachedplan.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.480.cachedplan.sqlpp
index 552c9f4af7..24a06e283f 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.480.cachedplan.sqlpp
@@ -16,21 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
+// requesttype=application/json
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a plan whose time travel value read a clock is not cached: a cached plan would keep reading the first run's
+// clock. Run twice; neither run may be served from the plan cache
+SELECT count(*) AS count FROM usersNoSnapshot AT timestamp current_datetime_immediate();
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.490.cachedplan.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.490.cachedplan.sqlpp
index 552c9f4af7..24a06e283f 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.490.cachedplan.sqlpp
@@ -16,21 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
+// requesttype=application/json
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a plan whose time travel value read a clock is not cached: a cached plan would keep reading the first run's
+// clock. Run twice; neither run may be served from the plan cache
+SELECT count(*) AS count FROM usersNoSnapshot AT timestamp current_datetime_immediate();
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.495.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.495.ddl.sqlpp
index 552c9f4af7..65731c51bd 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/iceberg/snapshots/test.495.ddl.sqlpp
@@ -17,20 +17,4 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+DROP FUNCTION countAt@1;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.001.delete.http
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.001.delete.http
index 552c9f4af7..ebcef2721b 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.001.delete.http
@@ -16,21 +16,4 @@
* specific language governing permissions and limitations
* under the License.
*/
-
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+/admin/plancache
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.002.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.002.ddl.sqlpp
index 552c9f4af7..6b0eda58bb 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.002.ddl.sqlpp
@@ -17,20 +17,8 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+DROP DATAVERSE ttpc IF EXISTS;
+CREATE DATAVERSE ttpc;
+USE ttpc;
+CREATE TYPE T AS { uid: integer };
+CREATE DATASET ds(T) PRIMARY KEY uid;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.003.cachedplan.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.003.cachedplan.sqlpp
index 552c9f4af7..a36e05aa97 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.003.cachedplan.sqlpp
@@ -16,21 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
+// requesttype=application/json
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a clock in a time travel value is read while the query is compiled, so its plan must not be reused:
+// a cached plan would keep reading the first run's clock => never a cached plan
+SELECT VALUE count(*) FROM ttpc.ds AT TIMESTAMP current_datetime_immediate();
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.004.cachedplan.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.004.cachedplan.sqlpp
index 552c9f4af7..a36e05aa97 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.004.cachedplan.sqlpp
@@ -16,21 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
+// requesttype=application/json
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a clock in a time travel value is read while the query is compiled, so its plan must not be reused:
+// a cached plan would keep reading the first run's clock => never a cached plan
+SELECT VALUE count(*) FROM ttpc.ds AT TIMESTAMP current_datetime_immediate();
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.005.cachedplan.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.005.cachedplan.sqlpp
index 552c9f4af7..cf61242189 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.005.cachedplan.sqlpp
@@ -16,21 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+// requesttype=application/json
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a literal time travel value is the same on every run, so its plan is still reused => cached on the second run
+SELECT VALUE count(*) FROM ttpc.ds AT TIMESTAMP "2026-01-01T00:00:00";
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.006.cachedplan.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.006.cachedplan.sqlpp
index 552c9f4af7..cf61242189 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.006.cachedplan.sqlpp
@@ -16,21 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+// requesttype=application/json
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a literal time travel value is the same on every run, so its plan is still reused => cached on the second run
+SELECT VALUE count(*) FROM ttpc.ds AT TIMESTAMP "2026-01-01T00:00:00";
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.007.cachedplan.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.007.cachedplan.sqlpp
index 552c9f4af7..a8666380d7 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.007.cachedplan.sqlpp
@@ -16,21 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
+// requesttype=application/json
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a value that is not a literal but is functionally constant folds the same way on every run, so its plan
+// is still reused => cached on the second run
+SELECT VALUE count(*) FROM ttpc.ds AT TIMESTAMP unix_time_from_datetime_in_ms(datetime("2026-01-01T00:00:00"));
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.008.cachedplan.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.008.cachedplan.sqlpp
index 552c9f4af7..a8666380d7 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.008.cachedplan.sqlpp
@@ -16,21 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
+// requesttype=application/json
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a value that is not a literal but is functionally constant folds the same way on every run, so its plan
+// is still reused => cached on the second run
+SELECT VALUE count(*) FROM ttpc.ds AT TIMESTAMP unix_time_from_datetime_in_ms(datetime("2026-01-01T00:00:00"));
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.009.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.009.ddl.sqlpp
index 552c9f4af7..f88b247f7f 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/metrics/plan-cache-time-travel/plan-cache-time-travel.009.ddl.sqlpp
@@ -17,20 +17,4 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+DROP DATAVERSE ttpc;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.1.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.1.ddl.sqlpp
index 552c9f4af7..a4881f3907 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.1.ddl.sqlpp
@@ -17,20 +17,8 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+drop dataverse tt if exists;
+create dataverse tt;
+use tt;
+create type tt.T as { uid : integer };
+create dataset ds(T) primary key uid;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.10.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.10.query.sqlpp
index 552c9f4af7..af2654e731 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.10.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+use tt;
+// nor can a boolean
+// expect: ASX1265, reporting a boolean
+select value d.uid from ds as d AT SNAPSHOT true;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.11.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.11.query.sqlpp
index 552c9f4af7..dcf3aea2d3 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.11.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+use tt;
+// the JOIN clause validates its own value, not only the FROM term
+// expect: ASX1266, raised for the JOIN clause rather than the FROM term
+select value d.uid from ds as d join ds as e AT SNAPSHOT get_job_param("x") on d.uid = e.uid;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.12.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.12.query.sqlpp
index 552c9f4af7..5b8c07e74a 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.12.query.sqlpp
@@ -17,20 +17,11 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
+use tt;
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// the value is resolved before the FROM term opens its own scope, so the term cannot see the
+// variable it is itself binding. Expect ASX1073 "undefined identifier d" pointing at the value,
+// not ASX1106 "expected constant value" pointing at the whole clause: had the value been resolved
+// inside the scope, d would bind to the term's own item and become a per-row field access, which
+// fails much later and much less helpfully
+select value d.uid from ds as d AT SNAPSHOT d.uid;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.13.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.13.query.sqlpp
index 552c9f4af7..58d007b934 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.13.query.sqlpp
@@ -17,20 +17,9 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
+use tt;
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a variable bound by a *preceding* FROM term IS in scope here, so x resolves cleanly. Expect
+// ASX1106 "expected constant value" instead: a per-row value can never be folded. This is the
+// free-variable path rather than the unresolved-identifier one in case 12
+select value d.uid from ds as x, ds as d AT SNAPSHOT x.uid;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.14.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.14.query.sqlpp
index 552c9f4af7..c52a309c60 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.14.query.sqlpp
@@ -17,20 +17,9 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
+use tt;
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// the grammar accepts a value and a positional variable on the same term, since AT introduces
+// both. Expect ASX1079: time travel needs a collection, and positional variables are rejected
+// over collections, so this combination parses but can never execute
+select value d.uid from ds as d AT SNAPSHOT 1 AT p;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.15.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.15.query.sqlpp
index 552c9f4af7..d29fb5f199 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.15.query.sqlpp
@@ -17,20 +17,9 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+use tt;
+// every function in the value is evaluated while the query is compiled, uuid() included; the value it
+// yields is of type uuid, which is neither an integer nor a string, so it is rejected before anything
+// could try to use it as a snapshot id
+// expect: ASX1265, reporting uuid
+select value d.uid from ds as d AT SNAPSHOT uuid();
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.16.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.16.ddl.sqlpp
index 552c9f4af7..b8e46caf70 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.16.ddl.sqlpp
@@ -17,20 +17,4 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+drop dataverse tt if exists;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.2.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.2.query.sqlpp
index 552c9f4af7..fd59a86ac3 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.2.query.sqlpp
@@ -17,20 +17,8 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+use tt;
+// a time travel value is resolved while the query is compiled, and a job parameter exists only once the
+// job runs, so this value can never be produced here
+// expect: ASX1266, naming the value that could not be resolved
+select value d.uid from ds as d AT SNAPSHOT get_job_param("x");
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.3.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.3.query.sqlpp
index 552c9f4af7..27694c1816 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.3.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+use tt;
+// a snapshot id is an integer, not a date
+// expect: ASX1265, reporting a date where an integer or string is required
+select value d.uid from ds as d AT SNAPSHOT date("2026-08-20");
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.4.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.4.query.sqlpp
index 552c9f4af7..0b9e99fb73 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.4.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+use tt;
+// date arithmetic takes a duration, not a number; the mismatch folds to null
+// expect: ASX1265, reporting null -- the date/number mismatch folds to null before it is checked
+select value d.uid from ds as d AT TIMESTAMP date("2026-08-20") - 1;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.5.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.5.query.sqlpp
index 552c9f4af7..bcf1dcc1c4 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.5.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+use tt;
+// a fractional number cannot identify a snapshot
+// expect: ASX1265, reporting a double
+select value d.uid from ds as d AT TIMESTAMP 1.5;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.6.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.6.query.sqlpp
index 552c9f4af7..4b58399062 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.6.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+use tt;
+// a subquery needs operators of its own, so it is not a compile-time constant
+// expect: ASX1106 "Expected constant value" -- the subquery needs operators of its own
+select value d.uid from ds as d AT SNAPSHOT (select value 1 from ds);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.7.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.7.query.sqlpp
index 552c9f4af7..8296c2ce09 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.7.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+use tt;
+// no value was supplied for the parameter
+// expect: ASX1086 "No value for parameter: $ts"
+select value d.uid from ds as d AT SNAPSHOT $ts;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.8.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.8.query.sqlpp
index 552c9f4af7..59ae4087bd 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.8.query.sqlpp
@@ -17,20 +17,8 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+use tt;
+// the current_* functions read the job start time, so their value is not known while the query is
+// being compiled; the message names the function that can be used instead
+// expect: ASX1267, pointing at current_date_immediate()
+select value d.uid from ds as d AT TIMESTAMP current_date() - duration("P3M");
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.9.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.9.query.sqlpp
index 552c9f4af7..5fee8e7792 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-negative/time-travel-negative.9.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+use tt;
+// a bare time of day cannot identify a snapshot
+// expect: ASX1265, reporting a time
+select value d.uid from ds as d AT TIMESTAMP time("12:00:00");
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.1.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.1.ddl.sqlpp
index 552c9f4af7..fa9d8c0ebc 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.1.ddl.sqlpp
@@ -17,20 +17,9 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// values that need operators of their own when translated, rather than an expression an ASSIGN can carry
+drop dataverse ttna if exists;
+create dataverse ttna;
+use ttna;
+create type T as { uid : integer };
+create dataset ds(T) primary key uid;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.2.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.2.query.sqlpp
index 552c9f4af7..a32074ba91 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.2.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a quantified expression over a constant array is rewritten to a function call => resolves
+use ttna;
+select value count(*) from ds at snapshot CASE WHEN (SOME x IN [1,2] SATISFIES x = 2) THEN 5 ELSE 6 END;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.3.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.3.query.sqlpp
index 552c9f4af7..95c9cc5c18 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.3.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// membership in a constant array is a function call => resolves
+use ttna;
+select value count(*) from ds at snapshot CASE WHEN 2 IN [1,2] THEN 5 ELSE 6 END;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.4.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.4.query.sqlpp
index 552c9f4af7..166185d16b 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.4.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a function over a constant array folds => resolves
+use ttna;
+select value count(*) from ds at snapshot array_count([1,2,3]);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.5.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.5.query.sqlpp
index 552c9f4af7..be8eaba0a8 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.5.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// membership in a subquery needs a subplan of its own, so the value is not a constant
+// expect: ASX1106, expected constant value
+use ttna;
+select value count(*) from ds at snapshot CASE WHEN 2 IN (SELECT VALUE v FROM [1,2] v) THEN 5 ELSE 6 END;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.6.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.6.query.sqlpp
index 552c9f4af7..f323364fc5 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.6.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// EXISTS over a subquery likewise needs its own operators
+// expect: ASX1106, expected constant value
+use ttna;
+select value count(*) from ds at snapshot CASE WHEN EXISTS (SELECT VALUE 1) THEN 5 ELSE 6 END;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.7.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.7.ddl.sqlpp
index 552c9f4af7..e6631667ae 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-non-assign/time-travel-non-assign.7.ddl.sqlpp
@@ -17,20 +17,4 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+drop dataverse ttna;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.1.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.1.ddl.sqlpp
index 552c9f4af7..3f776a61f1 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.1.ddl.sqlpp
@@ -17,20 +17,11 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a function that takes its time travel value as a parameter: the argument has to be substituted into the
+// value when the body is inlined, or the value refers to a variable that no longer exists
+drop dataverse ttu if exists;
+create dataverse ttu;
+use ttu;
+create type T as { uid : integer };
+create dataset ds(T) primary key uid;
+create function atTs(ts) { (select value count(*) from ds at timestamp ts)[0] };
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.2.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.2.query.sqlpp
index 552c9f4af7..c24295c3a4 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.2.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a literal argument resolves the value
+use ttu;
+select value atTs("2026-01-01T00:00:00");
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.3.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.3.query.sqlpp
index 552c9f4af7..395fa30469 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.3.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a clock argument is read while the query is compiled, like a clock written in place
+use ttu;
+select value atTs(current_datetime_immediate());
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.4.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.4.query.sqlpp
index 552c9f4af7..a72a3bc99a 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.4.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// the argument is what the value checks: a boolean is rejected as a time travel value
+// expect: ASX1265, reporting boolean
+use ttu;
+select value atTs(true);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.5.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.5.ddl.sqlpp
index 552c9f4af7..7e38de2c02 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-udf-param/time-travel-udf-param.5.ddl.sqlpp
@@ -17,20 +17,4 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+drop dataverse ttu;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.1.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.1.ddl.sqlpp
index 552c9f4af7..82195972c3 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.1.ddl.sqlpp
@@ -17,20 +17,9 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// values whose type is only known as a union or ANY, or that read a field of a constant object
+drop dataverse ttvt if exists;
+create dataverse ttvt;
+use ttvt;
+create type T as { uid : integer };
+create dataset ds(T) primary key uid;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.2.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.2.query.sqlpp
index 552c9f4af7..dd4d261494 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.2.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// an index into a constant array folds to its element => resolves
+use ttvt;
+select value count(*) from ds at snapshot [5][0];
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.3.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.3.query.sqlpp
index 552c9f4af7..fd63313a92 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.3.query.sqlpp
@@ -17,20 +17,6 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// a function whose declared return type is a union folds on the constant it is given => resolves
+use ttvt;
+select value count(*) from ds at snapshot if_missing_or_null(null, 5);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.4.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.4.query.sqlpp
index 552c9f4af7..2ca1146da2 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.4.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// known limitation: a field of an open object is typed ANY, which constant folding leaves alone so the record stays open
+// expect: ASX1266, the value could not be resolved
+use ttvt;
+select value count(*) from ds at snapshot object_put({}, "id", 5).id;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.5.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.5.query.sqlpp
index 552c9f4af7..8bde77e438 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.5.query.sqlpp
@@ -17,20 +17,7 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// known limitation: branches of different types make the CASE a union, which constant folding leaves alone
+// expect: ASX1266, the value could not be resolved
+use ttvt;
+select value count(*) from ds at snapshot CASE WHEN true THEN 5 ELSE "x" END;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.6.query.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.6.query.sqlpp
index 552c9f4af7..d18ad7649e 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.6.query.sqlpp
@@ -17,20 +17,8 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+// known limitation: a field of a closed object is left for the rule that rewrites it to an index access, which runs
+// after the value is resolved
+// expect: ASX1266, the value could not be resolved
+use ttvt;
+select value count(*) from ds at snapshot {"id": 5}.id;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.7.ddl.sqlpp
similarity index 53%
copy from asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.7.ddl.sqlpp
index 552c9f4af7..ef78beecd3 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/time-travel-value-types/time-travel-value-types.7.ddl.sqlpp
@@ -17,20 +17,4 @@
* under the License.
*/
-package org.apache.asterix.runtime.evaluators.functions.temporal;
-
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.api.context.IEvaluatorContext;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
- CurrentDateTimeImmediateEval(IEvaluatorContext ctx, SourceLocation sourceLoc, FunctionIdentifier fid) {
- super(ctx, sourceLoc, fid);
- }
-
- @Override
- protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
- }
-}
+drop dataverse ttvt;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.085.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.085.adm
new file mode 100644
index 0000000000..be08cd88c3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.085.adm
@@ -0,0 +1 @@
+{ "count": 800 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.090.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.090.adm
new file mode 100644
index 0000000000..be08cd88c3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.090.adm
@@ -0,0 +1 @@
+{ "count": 800 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.100.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.100.adm
new file mode 100644
index 0000000000..21e48a29db
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.100.adm
@@ -0,0 +1 @@
+{ "count": 1000 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.110.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.110.adm
new file mode 100644
index 0000000000..21e48a29db
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.110.adm
@@ -0,0 +1 @@
+{ "count": 1000 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.120.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.120.adm
new file mode 100644
index 0000000000..0d88a9d9cb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.120.adm
@@ -0,0 +1 @@
+{ "count": 997 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.140.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.140.adm
new file mode 100644
index 0000000000..29ae63f497
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.140.adm
@@ -0,0 +1 @@
+{ "s1": 800, "s2": 1000, "latest": 997 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.150.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.150.adm
new file mode 100644
index 0000000000..4de53e209e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.150.adm
@@ -0,0 +1 @@
+{ "count": 797 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.160.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.160.adm
new file mode 100644
index 0000000000..be08cd88c3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.160.adm
@@ -0,0 +1 @@
+{ "count": 800 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.170.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.170.adm
new file mode 100644
index 0000000000..0d88a9d9cb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.170.adm
@@ -0,0 +1 @@
+{ "count": 997 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.180.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.180.adm
new file mode 100644
index 0000000000..be08cd88c3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.180.adm
@@ -0,0 +1 @@
+{ "count": 800 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.190.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.190.adm
new file mode 100644
index 0000000000..be08cd88c3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.190.adm
@@ -0,0 +1 @@
+{ "count": 800 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.200.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.200.adm
new file mode 100644
index 0000000000..be08cd88c3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.200.adm
@@ -0,0 +1 @@
+{ "count": 800 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.220.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.220.adm
new file mode 100644
index 0000000000..be08cd88c3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.220.adm
@@ -0,0 +1 @@
+{ "count": 800 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.240.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.240.adm
new file mode 100644
index 0000000000..ffd8cad024
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.240.adm
@@ -0,0 +1 @@
+{ "matched": 800 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.260.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.260.adm
new file mode 100644
index 0000000000..ffd8cad024
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.260.adm
@@ -0,0 +1 @@
+{ "matched": 800 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.290.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.290.adm
new file mode 100644
index 0000000000..be08cd88c3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.290.adm
@@ -0,0 +1 @@
+{ "count": 800 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.310.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.310.adm
new file mode 100644
index 0000000000..a84cac2a54
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.310.adm
@@ -0,0 +1,2 @@
+{ "lo": false, "c": 400 }
+{ "lo": true, "c": 397 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.320.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.320.adm
new file mode 100644
index 0000000000..17f5935cba
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.320.adm
@@ -0,0 +1 @@
+{ "maxRn": 800 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.330.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.330.adm
new file mode 100644
index 0000000000..2decc569b7
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.330.adm
@@ -0,0 +1 @@
+{ "c": 1797 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.340.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.340.adm
new file mode 100644
index 0000000000..ffd8cad024
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.340.adm
@@ -0,0 +1 @@
+{ "matched": 800 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.360.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.360.adm
new file mode 100644
index 0000000000..eb31ab14eb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.360.adm
@@ -0,0 +1 @@
+{ "c": 797 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.370.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.370.adm
new file mode 100644
index 0000000000..d3ddb873c8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.370.adm
@@ -0,0 +1 @@
+{ "count": 997 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.380.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.380.adm
new file mode 100644
index 0000000000..d3ddb873c8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.380.adm
@@ -0,0 +1 @@
+{ "count": 997 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.390.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.390.adm
new file mode 100644
index 0000000000..d3ddb873c8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.390.adm
@@ -0,0 +1 @@
+{ "count": 997 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.400.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.400.adm
new file mode 100644
index 0000000000..2aa1cf0a6c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.400.adm
@@ -0,0 +1 @@
+{ "count": 800 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.410.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.410.adm
new file mode 100644
index 0000000000..d3ddb873c8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.410.adm
@@ -0,0 +1 @@
+{ "count": 997 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.420.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.420.adm
new file mode 100644
index 0000000000..d3ddb873c8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.420.adm
@@ -0,0 +1 @@
+{ "count": 997 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.430.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.430.adm
new file mode 100644
index 0000000000..86935c1185
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.430.adm
@@ -0,0 +1 @@
+{ "count": 1000 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.440.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.440.adm
new file mode 100644
index 0000000000..2aa1cf0a6c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.440.adm
@@ -0,0 +1 @@
+{ "count": 800 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.450.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.450.adm
new file mode 100644
index 0000000000..2aa1cf0a6c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.450.adm
@@ -0,0 +1 @@
+{ "count": 800 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.460.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.460.adm
new file mode 100644
index 0000000000..2aa1cf0a6c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.460.adm
@@ -0,0 +1 @@
+{ "count": 800 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.470.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.470.adm
new file mode 100644
index 0000000000..d3ddb873c8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.470.adm
@@ -0,0 +1 @@
+{ "count": 997 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.480.regexadm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.480.regexadm
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.480.regexadm
@@ -0,0 +1 @@
+false
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.490.regexadm b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.490.regexadm
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/iceberg/snapshots/result.490.regexadm
@@ -0,0 +1 @@
+false
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.001.regexjson b/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.001.regexjson
new file mode 100644
index 0000000000..4af8bfc4db
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.001.regexjson
@@ -0,0 +1 @@
+{"status":"R{[0-9]+ query plan cache entries have been cleared\\.}"}
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.003.regexadm b/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.003.regexadm
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.003.regexadm
@@ -0,0 +1 @@
+false
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.004.regexadm b/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.004.regexadm
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.004.regexadm
@@ -0,0 +1 @@
+false
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.005.regexadm b/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.005.regexadm
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.005.regexadm
@@ -0,0 +1 @@
+false
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.006.regexadm b/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.006.regexadm
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.006.regexadm
@@ -0,0 +1 @@
+true
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.007.regexadm b/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.007.regexadm
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.007.regexadm
@@ -0,0 +1 @@
+false
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.008.regexadm b/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.008.regexadm
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/metrics/plan-cache-time-travel/plan-cache-time-travel.008.regexadm
@@ -0,0 +1 @@
+true
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-non-assign/time-travel-non-assign.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-non-assign/time-travel-non-assign.2.adm
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-non-assign/time-travel-non-assign.2.adm
@@ -0,0 +1 @@
+0
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-non-assign/time-travel-non-assign.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-non-assign/time-travel-non-assign.3.adm
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-non-assign/time-travel-non-assign.3.adm
@@ -0,0 +1 @@
+0
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-non-assign/time-travel-non-assign.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-non-assign/time-travel-non-assign.4.adm
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-non-assign/time-travel-non-assign.4.adm
@@ -0,0 +1 @@
+0
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-udf-param/time-travel-udf-param.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-udf-param/time-travel-udf-param.2.adm
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-udf-param/time-travel-udf-param.2.adm
@@ -0,0 +1 @@
+0
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-udf-param/time-travel-udf-param.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-udf-param/time-travel-udf-param.3.adm
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-udf-param/time-travel-udf-param.3.adm
@@ -0,0 +1 @@
+0
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-value-types/time-travel-value-types.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-value-types/time-travel-value-types.2.adm
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-value-types/time-travel-value-types.2.adm
@@ -0,0 +1 @@
+0
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-value-types/time-travel-value-types.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-value-types/time-travel-value-types.3.adm
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/time-travel-value-types/time-travel-value-types.3.adm
@@ -0,0 +1 @@
+0
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml b/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml
index 0861b52ecc..b86e50d4d6 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml
@@ -7297,6 +7297,46 @@
</test-case>
</test-group>
<test-group name="misc">
+ <test-case FilePath="misc">
+ <compilation-unit name="time-travel-negative">
+ <output-dir compare="Text">time-travel-negative</output-dir>
+ <expected-error>ASX1266: Time travel value could not be resolved while compiling the query</expected-error>
+ <expected-error>ASX1265: Invalid 'AT SNAPSHOT' value: expected an integer or a string, but got date</expected-error>
+ <expected-error>ASX1265: Invalid 'AT TIMESTAMP' value: expected an integer, a string, a date or a datetime, but got null</expected-error>
+ <expected-error>ASX1265: Invalid 'AT TIMESTAMP' value: expected an integer, a string, a date or a datetime, but got double</expected-error>
+ <expected-error>ASX1106: Expected constant value</expected-error>
+ <expected-error>ASX1086: No value for parameter: $ts</expected-error>
+ <expected-error>ASX1267: current_date cannot be used in a time travel value; use current_date_immediate instead</expected-error>
+ <expected-error>ASX1265: Invalid 'AT TIMESTAMP' value: expected an integer, a string, a date or a datetime, but got time</expected-error>
+ <expected-error>ASX1265: Invalid 'AT SNAPSHOT' value: expected an integer or a string, but got boolean</expected-error>
+ <expected-error>ASX1266: Time travel value could not be resolved while compiling the query</expected-error>
+ <expected-error>ASX1073: Cannot resolve alias reference for undefined identifier d</expected-error>
+ <expected-error>ASX1106: Expected constant value</expected-error>
+ <expected-error>ASX1079: Compilation error: No positional variables are allowed over datasets</expected-error>
+ <expected-error>ASX1265: Invalid 'AT SNAPSHOT' value: expected an integer or a string, but got uuid</expected-error>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="misc">
+ <compilation-unit name="time-travel-udf-param">
+ <output-dir compare="Text">time-travel-udf-param</output-dir>
+ <expected-error>ASX1265: Invalid 'AT TIMESTAMP' value: expected an integer, a string, a date or a datetime, but got boolean</expected-error>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="misc">
+ <compilation-unit name="time-travel-value-types">
+ <output-dir compare="Text">time-travel-value-types</output-dir>
+ <expected-error>ASX1266: Time travel value could not be resolved while compiling the query</expected-error>
+ <expected-error>ASX1266: Time travel value could not be resolved while compiling the query</expected-error>
+ <expected-error>ASX1266: Time travel value could not be resolved while compiling the query</expected-error>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="misc">
+ <compilation-unit name="time-travel-non-assign">
+ <output-dir compare="Text">time-travel-non-assign</output-dir>
+ <expected-error>ASX1106: Expected constant value</expected-error>
+ <expected-error>ASX1106: Expected constant value</expected-error>
+ </compilation-unit>
+ </test-case>
<test-case FilePath="misc">
<compilation-unit name="big_in_list/000">
<output-dir compare="Text">big_in_list/000</output-dir>
@@ -16204,6 +16244,11 @@
<output-dir compare="Text">plan-cache-parameterized</output-dir>
</compilation-unit>
</test-case>
+ <test-case FilePath="metrics">
+ <compilation-unit name="plan-cache-time-travel">
+ <output-dir compare="Text">plan-cache-time-travel</output-dir>
+ </compilation-unit>
+ </test-case>
<test-case FilePath="metrics">
<compilation-unit name="plan-cache-ddl-replace">
<output-dir compare="Text">plan-cache-ddl-replace</output-dir>
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_iceberg.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_iceberg.xml
index c456a3ea7d..5044a052b5 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_iceberg.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_iceberg.xml
@@ -192,10 +192,13 @@
<test-case FilePath="iceberg/negative">
<compilation-unit name="bad-snapshot-format">
<output-dir compare="Text">bad-snapshot-format</output-dir>
- <expected-error>ASX1108: External source error. unexpected TIMESTAMP snapshot format. Allow formats are: (milliseconds, yyyy-MM-dd or yyyy-MM-dd'T'HH:mm:ss). Found: Hello!</expected-error>
- <expected-error>ASX1108: External source error. unexpected TIMESTAMP snapshot format. Allow formats are: (milliseconds, yyyy-MM-dd or yyyy-MM-dd'T'HH:mm:ss). Found: 2024-12-12 11:11:11</expected-error>
- <expected-error>ASX1108: External source error. unexpected TIMESTAMP snapshot format. Allow formats are: (milliseconds, yyyy-MM-dd or yyyy-MM-dd'T'HH:mm:ss). Found: Hello!</expected-error>
- <expected-error>ASX1108: External source error. unexpected TIMESTAMP snapshot format. Allow formats are: (milliseconds, yyyy-MM-dd or yyyy-MM-dd'T'HH:mm:ss). Found: 2024-12-12 11:11:11.123</expected-error>
+ <expected-error>ASX1108: External source error. unexpected TIMESTAMP snapshot format. Allow formats are: (milliseconds, yyyy-MM-dd, yyyy-MM-dd'T'HH:mm:ss or yyyy-MM-dd'T'HH:mm:ssXXX). Found: Hello!</expected-error>
+ <expected-error>ASX1108: External source error. unexpected TIMESTAMP snapshot format. Allow formats are: (milliseconds, yyyy-MM-dd, yyyy-MM-dd'T'HH:mm:ss or yyyy-MM-dd'T'HH:mm:ssXXX). Found: 2024-12-12 11:11:11</expected-error>
+ <expected-error>ASX1108: External source error. unexpected TIMESTAMP snapshot format. Allow formats are: (milliseconds, yyyy-MM-dd, yyyy-MM-dd'T'HH:mm:ss or yyyy-MM-dd'T'HH:mm:ssXXX). Found: Hello!</expected-error>
+ <expected-error>ASX1108: External source error. unexpected TIMESTAMP snapshot format. Allow formats are: (milliseconds, yyyy-MM-dd, yyyy-MM-dd'T'HH:mm:ss or yyyy-MM-dd'T'HH:mm:ssXXX). Found: 2024-12-12 11:11:11.123</expected-error>
+ <expected-error>ASX1239: Invalid Iceberg snapshot value</expected-error>
+ <expected-error>ASX1108: External source error. unexpected TIMESTAMP snapshot format. Allow formats are: (milliseconds, yyyy-MM-dd, yyyy-MM-dd'T'HH:mm:ss or yyyy-MM-dd'T'HH:mm:ssXXX). Found: </expected-error>
+ <expected-error>ASX1108: External source error. java.lang.IllegalArgumentException: Cannot find a snapshot older than</expected-error>
</compilation-unit>
</test-case>
<test-case FilePath="iceberg/negative">
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
index 200dfb2313..45a3a0cecc 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
@@ -376,6 +376,9 @@ public enum ErrorCode implements IError {
VENDED_CREDENTIALS_UNSUPPORTED_CATALOG_SOURCE(1262),
ANALYZE_NOT_SUPPORTED_ON_EXTERNAL_DATASET(1263),
UNSUPPORTED_ICEBERG_TYPE(1264),
+ INVALID_TIME_TRAVEL_VALUE(1265),
+ TIME_TRAVEL_VALUE_NOT_RESOLVED(1266),
+ TIME_TRAVEL_VALUE_UNSUPPORTED_FUNCTION(1267),
// Feed errors
DATAFLOW_ILLEGAL_STATE(3001),
diff --git a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
index 232819cf7e..ad93ca443f 100644
--- a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
+++ b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
@@ -378,6 +378,9 @@
1262 = Iceberg catalog source '%1$s' cannot vend credentials
1263 = ANALYZE is not supported on external collection %1$s
1264 = Unsupported Iceberg type '%1$s' for column '%2$s'
+1265 = Invalid 'AT %1$s' value: expected %2$s, but got %3$s
+1266 = Time travel value could not be resolved while compiling the query: %1$s
+1267 = %1$s cannot be used in a time travel value; use %2$s instead
# Feed Errors
3001 = Illegal state.
diff --git a/asterixdb/asterix-doc/src/main/markdown/builtins/7_temporal.md b/asterixdb/asterix-doc/src/main/markdown/builtins/7_temporal.md
index a1cdda2f85..ded390ff9e 100644
--- a/asterixdb/asterix-doc/src/main/markdown/builtins/7_temporal.md
+++ b/asterixdb/asterix-doc/src/main/markdown/builtins/7_temporal.md
@@ -244,30 +244,69 @@
current_date()
- * Gets the current date.
+ * Gets the current date. The value is taken once, when the query starts running, and
+ every call to this function within the query returns that same value.
* Arguments: None
* Return Value:
- * a `date` value of the date when the function is called.
+ * a `date` value, the same for every row of the query.
### current_time ###
* Syntax:
current_time()
- * Get the current time
+ * Gets the current time. The value is taken once, when the query starts running, and
+ every call to this function within the query returns that same value.
* Arguments: None
* Return Value:
- * a `time` value of the time when the function is called.
+ * a `time` value, the same for every row of the query.
### current_datetime ###
* Syntax:
current_datetime()
- * Get the current datetime
+ * Gets the current datetime. The value is taken once, when the query starts running, and
+ every call to this function within the query returns that same value.
* Arguments: None
* Return Value:
- * a `datetime` value of the datetime when the function is called.
+ * a `datetime` value, the same for every row of the query.
+
+### current_date_immediate ###
+ * Syntax:
+
+ current_date_immediate()
+
+ * Gets the current date, read from the clock at the moment the call is evaluated. A single
+ value for the query is not guaranteed: separate calls may return different values.
+ * Arguments: None
+ * Return Value:
+ * a `date` value.
+
+### current_time_immediate ###
+ * Syntax:
+
+ current_time_immediate()
+
+ * Gets the current time, read from the clock at the moment the call is evaluated. As with
+ `current_date_immediate()`, a single value for the query is not guaranteed.
+ * Arguments: None
+ * Return Value:
+ * a `time` value.
+
+### current_datetime_immediate ###
+ * Syntax:
+
+ current_datetime_immediate()
+
+ * Gets the current datetime, read from the clock at the moment the call is evaluated. As with
+ `current_date_immediate()`, a single value for the query is not guaranteed.
+ * In an `AT SNAPSHOT` / `AT TIMESTAMP` value, which is computed while the query is compiled, this
+ function and `current_date_immediate()` are taken in UTC, the zone a time travel timestamp is read
+ in, so that they select the current instant.
+ * Arguments: None
+ * Return Value:
+ * a `datetime` value.
### get_date_from_datetime ###
diff --git a/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/util/iceberg/IcebergSnapshotUtils.java b/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/util/iceberg/IcebergSnapshotUtils.java
index b2a2cdf93e..a66655bd22 100644
--- a/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/util/iceberg/IcebergSnapshotUtils.java
+++ b/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/util/iceberg/IcebergSnapshotUtils.java
@@ -20,6 +20,7 @@ package org.apache.asterix.external.util.iceberg;
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME;
+import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
import static org.apache.asterix.common.exceptions.ErrorCode.EXTERNAL_SOURCE_ERROR;
import static org.apache.asterix.common.exceptions.ErrorCode.PARAM_NOT_ALLOWED_IF_PARAM_IS_PRESENT;
import static org.apache.asterix.external.util.iceberg.IcebergConstants.ICEBERG_SNAPSHOT_ID_PROPERTY_KEY;
@@ -28,6 +29,7 @@ import static org.apache.hyracks.api.util.ExceptionUtils.getMessageOrToString;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeParseException;
import java.util.Map;
@@ -83,10 +85,17 @@ public class IcebergSnapshotUtils {
// try parsing as ISO 8601 timestamp, e.g., "yyyy-MM-dd'T'HH:mm:ss"
LocalDateTime localDateTime = LocalDateTime.parse(timestamp, ISO_LOCAL_DATE_TIME);
return localDateTime.atZone(ZoneOffset.UTC).toInstant().toEpochMilli();
+ } catch (DateTimeParseException ignored) {
+ }
+
+ try {
+ // the same with a zone offset, e.g., "yyyy-MM-dd'T'HH:mm:ssZ" or "yyyy-MM-dd'T'HH:mm:ss+03:00", which
+ // is how a clock function that renders a zone writes the current time
+ return OffsetDateTime.parse(timestamp, ISO_OFFSET_DATE_TIME).toInstant().toEpochMilli();
} catch (DateTimeParseException ignored) {
throw CompilationException.create(EXTERNAL_SOURCE_ERROR,
- "unexpected TIMESTAMP snapshot format. Allow formats are: (milliseconds, yyyy-MM-dd or "
- + "yyyy-MM-dd'T'HH:mm:ss). Found: " + timestamp);
+ "unexpected TIMESTAMP snapshot format. Allow formats are: (milliseconds, yyyy-MM-dd, "
+ + "yyyy-MM-dd'T'HH:mm:ss or yyyy-MM-dd'T'HH:mm:ssXXX). Found: " + timestamp);
}
}
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java
index de29621bdc..5080d67b60 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java
@@ -20,6 +20,7 @@
package org.apache.asterix.lang.common.util;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
public class CommonFunctionMapUtil {
@@ -265,4 +266,12 @@ public class CommonFunctionMapUtil {
public static void addFunctionMapping(String alias, String functionName) {
FUNCTION_NAME_MAP.put(alias, functionName);
}
+
+ /**
+ * @return every alias that maps to {@code functionName}, sorted; empty if it has none
+ */
+ public static List<String> getAliases(String functionName) {
+ return FUNCTION_NAME_MAP.entrySet().stream().filter(e -> e.getValue().equals(functionName))
+ .map(Map.Entry::getKey).sorted().toList();
+ }
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/JoinClauseRightInput.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/JoinClauseRightInput.java
index a454654aad..e6b0ed367d 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/JoinClauseRightInput.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/JoinClauseRightInput.java
@@ -20,16 +20,16 @@ package org.apache.asterix.lang.sqlpp;
import org.apache.asterix.lang.common.base.Expression;
import org.apache.asterix.lang.common.expression.VariableExpr;
-import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
public class JoinClauseRightInput {
private final Expression rightExpr;
private final VariableExpr rightVar;
private final VariableExpr posVar;
- private final TimeTravel timeTravel;
+ private final TimeTravelSpec timeTravel;
public JoinClauseRightInput(Expression rightExpr, VariableExpr rightVar, VariableExpr posVar,
- TimeTravel timeTravel) {
+ TimeTravelSpec timeTravel) {
this.rightExpr = rightExpr;
this.rightVar = rightVar;
this.posVar = posVar;
@@ -48,7 +48,7 @@ public class JoinClauseRightInput {
return posVar;
}
- public TimeTravel getTimeTravel() {
+ public TimeTravelSpec getTimeTravel() {
return timeTravel;
}
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateClause.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateClause.java
index 8c178f065e..f50fe18ed0 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateClause.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateClause.java
@@ -24,21 +24,21 @@ import java.util.Objects;
import org.apache.asterix.lang.common.base.AbstractClause;
import org.apache.asterix.lang.common.base.Expression;
import org.apache.asterix.lang.common.expression.VariableExpr;
-import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
public abstract class AbstractBinaryCorrelateClause extends AbstractClause {
private Expression rightExpr;
private final VariableExpr rightVar;
private final VariableExpr rightPosVar;
- private final TimeTravel timeTravel;
+ private TimeTravelSpec timeTravel;
public AbstractBinaryCorrelateClause(Expression rightExpr, VariableExpr rightVar, VariableExpr rightPosVar) {
this(rightExpr, rightVar, rightPosVar, null);
}
public AbstractBinaryCorrelateClause(Expression rightExpr, VariableExpr rightVar, VariableExpr rightPosVar,
- TimeTravel timeTravel) {
+ TimeTravelSpec timeTravel) {
this.rightExpr = rightExpr;
this.rightVar = rightVar;
this.rightPosVar = rightPosVar;
@@ -69,10 +69,14 @@ public abstract class AbstractBinaryCorrelateClause extends AbstractClause {
return timeTravel != null;
}
- public TimeTravel getTimeTravel() {
+ public TimeTravelSpec getTimeTravel() {
return timeTravel;
}
+ public void setTimeTravel(TimeTravelSpec timeTravel) {
+ this.timeTravel = timeTravel;
+ }
+
@Override
public int hashCode() {
return Objects.hash(rightExpr, rightPosVar, rightVar, timeTravel);
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateWithConditionClause.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateWithConditionClause.java
index cfebe24097..8434c08c59 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateWithConditionClause.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/AbstractBinaryCorrelateWithConditionClause.java
@@ -21,14 +21,14 @@ package org.apache.asterix.lang.sqlpp.clause;
import org.apache.asterix.lang.common.base.Expression;
import org.apache.asterix.lang.common.expression.VariableExpr;
-import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
public abstract class AbstractBinaryCorrelateWithConditionClause extends AbstractBinaryCorrelateClause {
private Expression conditionExpr;
public AbstractBinaryCorrelateWithConditionClause(Expression rightExpr, VariableExpr rightVar,
- VariableExpr rightPosVar, Expression conditionExpr, TimeTravel timeTravel) {
+ VariableExpr rightPosVar, Expression conditionExpr, TimeTravelSpec timeTravel) {
super(rightExpr, rightVar, rightPosVar, timeTravel);
this.conditionExpr = conditionExpr;
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromTerm.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromTerm.java
index 09bb2a56c7..57d4c41ead 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromTerm.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/FromTerm.java
@@ -28,15 +28,15 @@ import org.apache.asterix.lang.common.base.AbstractClause;
import org.apache.asterix.lang.common.base.Expression;
import org.apache.asterix.lang.common.expression.VariableExpr;
import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
-import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
public class FromTerm extends AbstractClause {
private Expression leftExpr;
private final VariableExpr leftVar;
private final VariableExpr posVar;
private final List<AbstractBinaryCorrelateClause> correlateClauses = new ArrayList<>();
- private final TimeTravel timeTravel;
+ private TimeTravelSpec timeTravel;
public FromTerm(Expression leftExpr, VariableExpr leftVar, VariableExpr posVar,
List<AbstractBinaryCorrelateClause> correlateClauses) {
@@ -44,7 +44,7 @@ public class FromTerm extends AbstractClause {
}
public FromTerm(Expression leftExpr, VariableExpr leftVar, VariableExpr posVar,
- List<AbstractBinaryCorrelateClause> correlateClauses, TimeTravel timeTravel) {
+ List<AbstractBinaryCorrelateClause> correlateClauses, TimeTravelSpec timeTravel) {
this.leftExpr = leftExpr;
this.leftVar = leftVar;
this.posVar = posVar;
@@ -96,10 +96,14 @@ public class FromTerm extends AbstractClause {
return timeTravel != null;
}
- public TimeTravel getTimeTravel() {
+ public TimeTravelSpec getTimeTravel() {
return timeTravel;
}
+ public void setTimeTravel(TimeTravelSpec timeTravel) {
+ this.timeTravel = timeTravel;
+ }
+
@Override
public String toString() {
return leftExpr + " AS " + leftVar;
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/JoinClause.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/JoinClause.java
index ca0ad700f9..32b5bdc91f 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/JoinClause.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/clause/JoinClause.java
@@ -27,8 +27,8 @@ import org.apache.asterix.lang.common.base.Literal;
import org.apache.asterix.lang.common.expression.VariableExpr;
import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
import org.apache.asterix.lang.sqlpp.optype.JoinType;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
-import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
public class JoinClause extends AbstractBinaryCorrelateWithConditionClause {
@@ -42,7 +42,7 @@ public class JoinClause extends AbstractBinaryCorrelateWithConditionClause {
}
public JoinClause(JoinType joinType, Expression rightExpr, VariableExpr rightVar, VariableExpr rightPosVar,
- Expression conditionExpr, Literal.Type outerJoinMissingValueType, TimeTravel timeTravel) {
+ Expression conditionExpr, Literal.Type outerJoinMissingValueType, TimeTravelSpec timeTravel) {
super(rightExpr, rightVar, rightPosVar, conditionExpr, timeTravel);
this.joinType = joinType;
setOuterJoinMissingValueType(outerJoinMissingValueType);
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGatherFunctionCallsVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGatherFunctionCallsVisitor.java
index d87da4a8fc..1fe48b36b2 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGatherFunctionCallsVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppGatherFunctionCallsVisitor.java
@@ -48,6 +48,7 @@ import org.apache.asterix.lang.sqlpp.expression.ChangeExpression;
import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
import org.apache.asterix.lang.sqlpp.expression.WindowExpression;
import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
import org.apache.commons.lang3.tuple.Pair;
@@ -69,6 +70,7 @@ public final class SqlppGatherFunctionCallsVisitor extends GatherFunctionCallsVi
@Override
public Void visit(FromTerm fromTerm, Void arg) throws CompilationException {
fromTerm.getLeftExpression().accept(this, arg);
+ gatherTimeTravelCalls(fromTerm.getTimeTravel(), arg);
for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) {
correlateClause.accept(this, arg);
}
@@ -78,6 +80,7 @@ public final class SqlppGatherFunctionCallsVisitor extends GatherFunctionCallsVi
@Override
public Void visit(JoinClause joinClause, Void arg) throws CompilationException {
joinClause.getRightExpression().accept(this, arg);
+ gatherTimeTravelCalls(joinClause.getTimeTravel(), arg);
joinClause.getConditionExpression().accept(this, arg);
return null;
}
@@ -289,4 +292,10 @@ public final class SqlppGatherFunctionCallsVisitor extends GatherFunctionCallsVi
}
return null;
}
+
+ private void gatherTimeTravelCalls(TimeTravelSpec timeTravel, Void arg) throws CompilationException {
+ if (timeTravel != null) {
+ timeTravel.getValueExpression().accept(this, arg);
+ }
+ }
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java
index 586dce02ae..a32594b54b 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppInlineUdfsVisitor.java
@@ -55,6 +55,7 @@ import org.apache.asterix.lang.sqlpp.expression.ChangeExpression;
import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
import org.apache.asterix.lang.sqlpp.expression.WindowExpression;
import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
import org.apache.asterix.lang.sqlpp.util.SqlppRewriteUtil;
import org.apache.asterix.lang.sqlpp.visitor.SqlppCloneAndSubstituteVariablesVisitor;
import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
@@ -97,6 +98,7 @@ public class SqlppInlineUdfsVisitor extends AbstractInlineUdfsVisitor implements
Pair<Boolean, Expression> p = inlineUdfsAndViewsInExpr(fromTerm.getLeftExpression());
fromTerm.setLeftExpression(p.getRight());
changed |= p.getLeft();
+ changed |= inlineUdfsAndViewsInTimeTravel(fromTerm.getTimeTravel());
for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) {
changed |= correlateClause.accept(this, arg);
}
@@ -109,7 +111,8 @@ public class SqlppInlineUdfsVisitor extends AbstractInlineUdfsVisitor implements
joinClause.setRightExpression(p1.getRight());
Pair<Boolean, Expression> p2 = inlineUdfsAndViewsInExpr(joinClause.getConditionExpression());
joinClause.setConditionExpression(p2.getRight());
- return p1.getLeft() || p2.getLeft();
+ boolean timeTravelChanged = inlineUdfsAndViewsInTimeTravel(joinClause.getTimeTravel());
+ return p1.getLeft() || p2.getLeft() || timeTravelChanged;
}
@Override
@@ -345,4 +348,13 @@ public class SqlppInlineUdfsVisitor extends AbstractInlineUdfsVisitor implements
}
return varExprMap;
}
+
+ private boolean inlineUdfsAndViewsInTimeTravel(TimeTravelSpec timeTravel) throws CompilationException {
+ if (timeTravel == null) {
+ return false;
+ }
+ Pair<Boolean, Expression> p = inlineUdfsAndViewsInExpr(timeTravel.getValueExpression());
+ timeTravel.setValueExpression(p.getRight());
+ return p.getLeft();
+ }
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppRightJoinRewriteVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppRightJoinRewriteVisitor.java
index cc9a6ca784..b52b23a40e 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppRightJoinRewriteVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppRightJoinRewriteVisitor.java
@@ -52,11 +52,11 @@ import org.apache.asterix.lang.sqlpp.clause.UnnestClause;
import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
import org.apache.asterix.lang.sqlpp.optype.JoinType;
import org.apache.asterix.lang.sqlpp.struct.SetOperationInput;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
import org.apache.asterix.lang.sqlpp.util.SqlppRewriteUtil;
import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppSimpleExpressionVisitor;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
import org.apache.hyracks.api.exceptions.SourceLocation;
/**
@@ -150,7 +150,7 @@ public final class SqlppRightJoinRewriteVisitor extends AbstractSqlppSimpleExpre
Expression fromExpr = fromTerm.getLeftExpression();
VariableExpr fromVar = fromTerm.getLeftVariable();
VariableExpr fromPosVar = fromTerm.getPositionalVariable();
- TimeTravel fromTimeTravel = fromTerm.getTimeTravel();
+ TimeTravelSpec fromTimeTravel = fromTerm.getTimeTravel();
List<AbstractBinaryCorrelateClause> correlateClauses = fromTerm.getCorrelateClauses();
for (int i = 0; i < correlateClauses.size(); i++) {
@@ -163,7 +163,7 @@ public final class SqlppRightJoinRewriteVisitor extends AbstractSqlppSimpleExpre
Expression rightExpr = joinClause.getRightExpression();
VariableExpr rightVar = joinClause.getRightVariable();
VariableExpr rightPosVar = joinClause.getPositionalVariable();
- TimeTravel rightTimeTravel = joinClause.getTimeTravel();
+ TimeTravelSpec rightTimeTravel = joinClause.getTimeTravel();
Expression condExpr = joinClause.getConditionExpression();
Literal.Type outerMissingValueType = joinClause.getOuterJoinMissingValueType();
if (i == 0) {
@@ -251,11 +251,11 @@ public final class SqlppRightJoinRewriteVisitor extends AbstractSqlppSimpleExpre
newPrecedingClause = new JoinClause(joinPrecedingClause.getJoinType(),
joinPrecedingClause.getRightExpression(),
newVariableExpr(newPrecedingClauseRightVar, precedingClauseRightVarSourceLoc),
- newPrecedingClauseRightPosVar != null
- ? newVariableExpr(newPrecedingClauseRightPosVar,
- precedingClauseRightPosVarSourceLoc)
+ newPrecedingClauseRightPosVar != null ? newVariableExpr(
+ newPrecedingClauseRightPosVar, precedingClauseRightPosVarSourceLoc)
: null,
- newCondExpr, joinPrecedingClause.getOuterJoinMissingValueType());
+ newCondExpr, joinPrecedingClause.getOuterJoinMissingValueType(),
+ joinPrecedingClause.getTimeTravel());
newPrecedingClause.setSourceLocation(precedingClauseSourceLoc);
break;
case UNNEST_CLAUSE:
@@ -351,7 +351,7 @@ public final class SqlppRightJoinRewriteVisitor extends AbstractSqlppSimpleExpre
}
private Expression createRightSelectExpression(Expression fromExpr, VarIdentifier fromVar, VarIdentifier fromPosVar,
- List<AbstractBinaryCorrelateClause> correlateClauseList, TimeTravel timeTravel,
+ List<AbstractBinaryCorrelateClause> correlateClauseList, TimeTravelSpec timeTravel,
List<Projection> projectList, SourceLocation sourceLoc) {
FromTerm newFromTerm = new FromTerm(fromExpr, newVariableExpr(fromVar, sourceLoc),
fromPosVar != null ? newVariableExpr(fromPosVar, sourceLoc) : null, correlateClauseList, timeTravel);
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/TimeTravelSpec.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/TimeTravelSpec.java
new file mode 100644
index 0000000000..83cc41ee91
--- /dev/null
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/struct/TimeTravelSpec.java
@@ -0,0 +1,97 @@
+/*
+ * 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.asterix.lang.sqlpp.struct;
+
+import java.util.Objects;
+
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
+import org.apache.hyracks.api.exceptions.SourceLocation;
+
+/**
+ * The unresolved form of an {@code AT SNAPSHOT} / {@code AT TIMESTAMP} specification.
+ * <p>
+ * The value is carried as an {@link Expression} so that the language rewrites (function call resolution,
+ * operator-to-function-call conversion, UDF inlining, ...) can process it like any other expression. It must
+ * reduce to a compile-time constant: expression-to-plan translation folds it into the {@link TimeTravel} that
+ * the plan carries, because the snapshot selects which Iceberg manifest/data files the scan reads and therefore
+ * has to be known while the plan is still being built.
+ */
+public final class TimeTravelSpec {
+
+ private final TimeTravel.Type type;
+
+ private Expression valueExpr;
+
+ private SourceLocation sourceLoc;
+
+ public TimeTravelSpec(Expression valueExpr, TimeTravel.Type type) {
+ this.valueExpr = Objects.requireNonNull(valueExpr, "valueExpr");
+ this.type = Objects.requireNonNull(type, "type");
+ }
+
+ public Expression getValueExpression() {
+ return valueExpr;
+ }
+
+ public void setValueExpression(Expression valueExpr) {
+ this.valueExpr = Objects.requireNonNull(valueExpr, "valueExpr");
+ }
+
+ public TimeTravel.Type getType() {
+ return type;
+ }
+
+ public SourceLocation getSourceLocation() {
+ return sourceLoc;
+ }
+
+ public void setSourceLocation(SourceLocation sourceLoc) {
+ this.sourceLoc = sourceLoc;
+ }
+
+ /**
+ * @return the {@code SNAPSHOT} / {@code TIMESTAMP} keyword this specification was written with.
+ */
+ public String getKeyword() {
+ return type.getKeyword();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(valueExpr, type);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (!(object instanceof TimeTravelSpec target)) {
+ return false;
+ }
+ return type == target.type && Objects.equals(valueExpr, target.valueExpr);
+ }
+
+ @Override
+ public String toString() {
+ return type.getKeyword() + " " + valueExpr;
+ }
+}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
index 739a35a65f..574d413095 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
@@ -74,9 +74,9 @@ import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
import org.apache.asterix.lang.sqlpp.expression.WindowExpression;
import org.apache.asterix.lang.sqlpp.struct.SetOperationInput;
import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppQueryExpressionVisitor;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
public class DeepCopyVisitor extends AbstractSqlppQueryExpressionVisitor<ILangExpression, Void> {
@@ -98,7 +98,7 @@ public class DeepCopyVisitor extends AbstractSqlppQueryExpressionVisitor<ILangEx
VariableExpr fromVar = (VariableExpr) fromTerm.getLeftVariable().accept(this, arg);
VariableExpr positionVar = fromTerm.getPositionalVariable() == null ? null
: (VariableExpr) fromTerm.getPositionalVariable().accept(this, arg);
- TimeTravel timeTravel = fromTerm.getTimeTravel();
+ TimeTravelSpec timeTravel = copy(fromTerm.getTimeTravel(), arg);
// Visits join/unnest/nest clauses.
List<AbstractBinaryCorrelateClause> correlateClauses = new ArrayList<>();
@@ -118,7 +118,7 @@ public class DeepCopyVisitor extends AbstractSqlppQueryExpressionVisitor<ILangEx
: (VariableExpr) joinClause.getPositionalVariable().accept(this, arg);
Expression conditionExpresion = (Expression) joinClause.getConditionExpression().accept(this, arg);
JoinClause copy = new JoinClause(joinClause.getJoinType(), rightExpression, rightVar, rightPositionVar,
- conditionExpresion, joinClause.getOuterJoinMissingValueType());
+ conditionExpresion, joinClause.getOuterJoinMissingValueType(), copy(joinClause.getTimeTravel(), arg));
copy.setSourceLocation(joinClause.getSourceLocation());
return copy;
}
@@ -623,4 +623,14 @@ public class DeepCopyVisitor extends AbstractSqlppQueryExpressionVisitor<ILangEx
}
return newFieldList;
}
+
+ private TimeTravelSpec copy(TimeTravelSpec timeTravel, Void arg) throws CompilationException {
+ if (timeTravel == null) {
+ return null;
+ }
+ TimeTravelSpec copy = new TimeTravelSpec((Expression) timeTravel.getValueExpression().accept(this, arg),
+ timeTravel.getType());
+ copy.setSourceLocation(timeTravel.getSourceLocation());
+ return copy;
+ }
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
index effe21f6b6..08c3974e7d 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/FreeVariableVisitor.java
@@ -72,6 +72,7 @@ import org.apache.asterix.lang.sqlpp.expression.ChangeExpression;
import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
import org.apache.asterix.lang.sqlpp.expression.WindowExpression;
import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppQueryExpressionVisitor;
import org.apache.commons.lang3.tuple.Pair;
@@ -105,6 +106,7 @@ public class FreeVariableVisitor extends AbstractSqlppQueryExpressionVisitor<Voi
// Visit the left expression of a from term.
fromTerm.getLeftExpression().accept(this, freeVars);
+ collectTimeTravelFreeVariables(fromTerm.getTimeTravel(), freeVars);
// Adds binding variables.
bindingVariables.add(fromTerm.getLeftVariable());
@@ -122,6 +124,7 @@ public class FreeVariableVisitor extends AbstractSqlppQueryExpressionVisitor<Voi
clauseFreeVars.clear();
}
clause.getRightExpression().accept(this, clauseFreeVars);
+ collectTimeTravelFreeVariables(clause.getTimeTravel(), clauseFreeVars);
switch (clause.getClauseType()) {
case UNNEST_CLAUSE:
@@ -623,4 +626,16 @@ public class FreeVariableVisitor extends AbstractSqlppQueryExpressionVisitor<Voi
selectFreeVars.removeAll(gbyLetsBindingVars);
selectFreeVars.removeAll(clusterByBindingVars);
}
+
+ /**
+ * A time travel value has to be a compile-time constant, so it should not reference anything. Its free
+ * variables are still collected so that a reference is reported here rather than silently surviving into
+ * the rewrites that consume this information.
+ */
+ private void collectTimeTravelFreeVariables(TimeTravelSpec timeTravel, Collection<VariableExpr> freeVars)
+ throws CompilationException {
+ if (timeTravel != null) {
+ timeTravel.getValueExpression().accept(this, freeVars);
+ }
+ }
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java
index 8a33bda7d1..5d13121e61 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppAstPrintVisitor.java
@@ -93,9 +93,9 @@ public class SqlppAstPrintVisitor extends QueryPrintVisitor implements ISqlppVis
out.print(skip(step) + "AS ");
fromTerm.getLeftVariable().accept(this, 0);
if (fromTerm.hasTimeTravel()) {
- String type = fromTerm.getTimeTravel().getType().getKeyword().toUpperCase();
- String value = fromTerm.getTimeTravel().getSnapshotIdOrTimestamp();
- out.println(" AT " + type + " " + value);
+ out.print(" AT " + fromTerm.getTimeTravel().getKeyword().toUpperCase() + " ");
+ fromTerm.getTimeTravel().getValueExpression().accept(this, 0);
+ out.println();
}
if (fromTerm.hasPositionalVariable()) {
out.println(" AT");
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
index 0893417a13..832aa5b141 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppCloneAndSubstituteVariablesVisitor.java
@@ -59,6 +59,7 @@ import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
import org.apache.asterix.lang.sqlpp.expression.WindowExpression;
import org.apache.asterix.lang.sqlpp.struct.SetOperationInput;
import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor;
import org.apache.commons.lang3.tuple.Pair;
@@ -123,8 +124,8 @@ public class SqlppCloneAndSubstituteVariablesVisitor extends CloneAndSubstituteV
}
}
}
- FromTerm newFromTerm =
- new FromTerm(newLeftExpr, newLeftVar, newLeftPosVar, newCorrelateClauses, fromTerm.getTimeTravel());
+ FromTerm newFromTerm = new FromTerm(newLeftExpr, newLeftVar, newLeftPosVar, newCorrelateClauses,
+ copyTimeTravel(fromTerm.getTimeTravel(), env));
newFromTerm.setSourceLocation(fromTerm.getSourceLocation());
return Pair.of(newFromTerm, currentEnv);
}
@@ -150,8 +151,9 @@ public class SqlppCloneAndSubstituteVariablesVisitor extends CloneAndSubstituteV
// The condition can refer to the newRightVar and newRightPosVar.
Expression conditionExpr = (Expression) joinClause.getConditionExpression().accept(this, currentEnv).getLeft();
- JoinClause newJoinClause = new JoinClause(joinClause.getJoinType(), newRightExpr, newRightVar, newRightPosVar,
- conditionExpr, joinClause.getOuterJoinMissingValueType());
+ JoinClause newJoinClause =
+ new JoinClause(joinClause.getJoinType(), newRightExpr, newRightVar, newRightPosVar, conditionExpr,
+ joinClause.getOuterJoinMissingValueType(), copyTimeTravel(joinClause.getTimeTravel(), env));
newJoinClause.setSourceLocation(joinClause.getSourceLocation());
return Pair.of(newJoinClause, currentEnv);
}
@@ -509,4 +511,21 @@ public class SqlppCloneAndSubstituteVariablesVisitor extends CloneAndSubstituteV
newWinExpr.addHints(winExpr.getHints());
return Pair.of(newWinExpr, env);
}
+
+ /**
+ * Clones a time travel specification in {@code env}, the scope the value is resolved in: the enclosing one,
+ * before the clause's own binding variables. A value that only resolves to a constant later can still
+ * reference variables here -- a function parameter while a function body is being inlined -- and they must
+ * be substituted like anywhere else, or the clone refers to a variable that no longer exists.
+ */
+ private TimeTravelSpec copyTimeTravel(TimeTravelSpec timeTravel, VariableSubstitutionEnvironment env)
+ throws CompilationException {
+ if (timeTravel == null) {
+ return null;
+ }
+ TimeTravelSpec copy = new TimeTravelSpec(
+ (Expression) timeTravel.getValueExpression().accept(this, env).getLeft(), timeTravel.getType());
+ copy.setSourceLocation(timeTravel.getSourceLocation());
+ return copy;
+ }
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
index 99f5b4d3cc..8dd6753c8b 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppFormatPrintVisitor.java
@@ -88,9 +88,9 @@ public class SqlppFormatPrintVisitor extends FormatPrintVisitor implements ISqlp
out.print(" as ");
fromTerm.getLeftVariable().accept(this, step + 2);
if (fromTerm.hasTimeTravel()) {
- String type = fromTerm.getTimeTravel().getType().getKeyword();
- String value = fromTerm.getTimeTravel().getSnapshotIdOrTimestamp();
- out.println(" at " + type + " " + value);
+ out.print(" at " + fromTerm.getTimeTravel().getKeyword() + " ");
+ fromTerm.getTimeTravel().getValueExpression().accept(this, step + 2);
+ out.println();
}
if (fromTerm.hasPositionalVariable()) {
out.print(" at ");
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java
index 171959e41b..3ea95066da 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppContainsExpressionVisitor.java
@@ -66,6 +66,7 @@ import org.apache.asterix.lang.sqlpp.expression.ChangeExpression;
import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
import org.apache.asterix.lang.sqlpp.expression.WindowExpression;
import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
import org.apache.commons.lang3.tuple.Pair;
/**
@@ -90,6 +91,9 @@ public abstract class AbstractSqlppContainsExpressionVisitor<T>
if (visit(fromTerm.getLeftExpression(), arg)) {
return true;
}
+ if (visitTimeTravel(fromTerm.getTimeTravel(), arg)) {
+ return true;
+ }
if (fromTerm.hasCorrelateClauses()) {
for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) {
if (correlateClause.accept(this, arg)) {
@@ -102,7 +106,8 @@ public abstract class AbstractSqlppContainsExpressionVisitor<T>
@Override
public Boolean visit(JoinClause joinClause, T arg) throws CompilationException {
- return visit(joinClause.getRightExpression(), arg) || visit(joinClause.getConditionExpression(), arg);
+ return visit(joinClause.getRightExpression(), arg) || visitTimeTravel(joinClause.getTimeTravel(), arg)
+ || visit(joinClause.getConditionExpression(), arg);
}
@Override
@@ -358,4 +363,8 @@ public abstract class AbstractSqlppContainsExpressionVisitor<T>
}
return cc.hasWithOptions() && visit(cc.getWithOptions(), arg);
}
+
+ protected Boolean visitTimeTravel(TimeTravelSpec timeTravel, T arg) throws CompilationException {
+ return timeTravel != null && visit(timeTravel.getValueExpression(), arg);
+ }
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
index b67138b47e..19ca543034 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
@@ -109,6 +109,14 @@ public class AbstractSqlppExpressionScopingVisitor extends AbstractSqlppSimpleEx
// Visit the left expression of a from term.
fromTerm.setLeftExpression(visit(fromTerm.getLeftExpression(), fromTerm));
+ // Resolved before the new scope opens, so the value cannot see this term's own binding variables.
+ // Inside the scope, "FROM Users AS c AT SNAPSHOT c.uid" would bind c to the term's own item
+ // variable and the value would become a per-row field access, failing much later and much less
+ // helpfully -- ASX1106 "expected constant value" pointing at the whole clause, rather than ASX1073
+ // "undefined identifier c" pointing at the c. It would also put this term's own binding variable
+ // into its free variable set, which the rewrites that read that set have no reason to expect.
+ visitTimeTravel(fromTerm.getTimeTravel(), fromTerm);
+
scopeChecker.createNewScope();
// Registers the data item variable.
@@ -137,6 +145,10 @@ public class AbstractSqlppExpressionScopingVisitor extends AbstractSqlppSimpleEx
// we defer the check to the query optimizer.
joinClause.setRightExpression(visit(joinClause.getRightExpression(), joinClause));
+ // Before the right branch's variables are registered, for the same reason as in visit(FromTerm):
+ // "JOIN d AT SNAPSHOT d.x" must not bind d to the branch it is selecting the snapshot for.
+ visitTimeTravel(joinClause.getTimeTravel(), joinClause);
+
// Registers the data item variable.
VariableExpr rightVar = joinClause.getRightVariable();
addNewVarSymbolToScope(scopeChecker.getCurrentScope(), rightVar.getVar(), rightVar.getSourceLocation(),
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
index 07e8d6b13e..9cfa70e4ae 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
@@ -73,6 +73,7 @@ import org.apache.asterix.lang.sqlpp.expression.ChangeExpression;
import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
import org.apache.asterix.lang.sqlpp.expression.WindowExpression;
import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
import org.apache.commons.lang3.tuple.Pair;
public class AbstractSqlppSimpleExpressionVisitor
@@ -90,6 +91,7 @@ public class AbstractSqlppSimpleExpressionVisitor
public Expression visit(FromTerm fromTerm, ILangExpression arg) throws CompilationException {
// Visit the left expression of a from term.
fromTerm.setLeftExpression(visit(fromTerm.getLeftExpression(), arg));
+ visitTimeTravel(fromTerm.getTimeTravel(), arg);
// Visits join/unnest/nest clauses.
for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) {
@@ -101,6 +103,7 @@ public class AbstractSqlppSimpleExpressionVisitor
@Override
public Expression visit(JoinClause joinClause, ILangExpression arg) throws CompilationException {
joinClause.setRightExpression(visit(joinClause.getRightExpression(), arg));
+ visitTimeTravel(joinClause.getTimeTravel(), arg);
joinClause.setConditionExpression(visit(joinClause.getConditionExpression(), arg));
return null;
}
@@ -525,4 +528,16 @@ public class AbstractSqlppSimpleExpressionVisitor
}
return newExprList;
}
+
+ /**
+ * Visits the value expression of an {@code AT SNAPSHOT} / {@code AT TIMESTAMP} specification, if there is
+ * one. The value has to reduce to a compile-time constant, but it still goes through the regular expression
+ * rewrites (function call resolution, operator-to-function-call conversion, UDF inlining, ...) so that
+ * anything foldable actually gets a chance to fold.
+ */
+ protected void visitTimeTravel(TimeTravelSpec timeTravel, ILangExpression arg) throws CompilationException {
+ if (timeTravel != null) {
+ timeTravel.setValueExpression(visit(timeTravel.getValueExpression(), arg));
+ }
+ }
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index a50bf114cd..1f8144f310 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -211,6 +211,7 @@ import org.apache.asterix.lang.sqlpp.clause.SelectElement;
import org.apache.asterix.lang.sqlpp.clause.SelectRegular;
import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
import org.apache.asterix.lang.sqlpp.clause.UnnestClause;
import org.apache.asterix.lang.common.clause.WhereClause;
import org.apache.asterix.lang.sqlpp.expression.ChangeExpression;
@@ -745,6 +746,10 @@ class SQLPPParser extends ScopeChecker implements IParser {
return laIdentifier(1, image);
}
+ private boolean laTimeTravelKeyword() {
+ return laIdentifier(SNAPSHOT) || laIdentifier(TIMESTAMP);
+ }
+
private Token fetchHint(Token token, SqlppHint... expectedHints) {
Token hintToken = token.specialToken;
if (hintToken == null) {
@@ -6258,8 +6263,7 @@ FromTerm FromTerm() throws ParseException :
AbstractBinaryCorrelateClause correlateClause = null;
List<AbstractBinaryCorrelateClause> correlateClauses = new ArrayList<AbstractBinaryCorrelateClause>();
- String snapshotOrTimestamp = null;
- TimeTravel.Type timeTravelType = TimeTravel.Type.SNAPSHOT_ID;
+ TimeTravelSpec timeTravel = null;
}
{
leftExpr = Expression()
@@ -6275,29 +6279,14 @@ FromTerm FromTerm() throws ParseException :
(
<AT>
(
- LOOKAHEAD( { laIdentifier(SNAPSHOT) } )
- <IDENTIFIER> snapshotOrTimestamp = StringLiteral()
- { timeTravelType = TimeTravel.Type.SNAPSHOT_ID; }
- (
- <AT> posVar = Variable()
- )?
- | LOOKAHEAD( { laIdentifier(TIMESTAMP) } )
- <IDENTIFIER> snapshotOrTimestamp = StringLiteral()
- { timeTravelType = TimeTravel.Type.SNAPSHOT_TIMESTAMP; }
+ LOOKAHEAD( { laTimeTravelKeyword() } )
+ timeTravel = TimeTravelSpec()
(
<AT> posVar = Variable()
)?
| posVar = Variable()
(
- <AT>
- (
- LOOKAHEAD( { laIdentifier(SNAPSHOT) } )
- <IDENTIFIER> snapshotOrTimestamp = StringLiteral()
- { timeTravelType = TimeTravel.Type.SNAPSHOT_ID; }
- | LOOKAHEAD( { laIdentifier(TIMESTAMP) } )
- <IDENTIFIER> snapshotOrTimestamp = StringLiteral()
- { timeTravelType = TimeTravel.Type.SNAPSHOT_TIMESTAMP; }
- )
+ <AT> timeTravel = TimeTravelSpec()
)?
)
)?
@@ -6317,13 +6306,7 @@ FromTerm FromTerm() throws ParseException :
if (leftVar == null) {
leftVar = ExpressionToVariableUtil.getGeneratedVariable(leftExpr, true);
}
- FromTerm fromTerm;
- if (snapshotOrTimestamp != null) {
- TimeTravel timeTravel = new TimeTravel(snapshotOrTimestamp, timeTravelType);
- fromTerm = new FromTerm(leftExpr, leftVar, posVar, correlateClauses, timeTravel);
- } else {
- fromTerm = new FromTerm(leftExpr, leftVar, posVar, correlateClauses);
- }
+ FromTerm fromTerm = new FromTerm(leftExpr, leftVar, posVar, correlateClauses, timeTravel);
fromTerm.setSourceLocation(leftExpr.getSourceLocation());
return fromTerm;
}
@@ -6370,14 +6353,35 @@ JoinClause CrossJoinClause() throws ParseException :
}
}
+TimeTravelSpec TimeTravelSpec() throws ParseException :
+{
+ Token startToken = null;
+ Expression valueExpr = null;
+ TimeTravel.Type timeTravelType = null;
+}
+{
+ (
+ LOOKAHEAD( { laIdentifier(SNAPSHOT) } )
+ <IDENTIFIER> { startToken = token; timeTravelType = TimeTravel.Type.SNAPSHOT_ID; }
+ valueExpr = Expression()
+ | LOOKAHEAD( { laIdentifier(TIMESTAMP) } )
+ <IDENTIFIER> { startToken = token; timeTravelType = TimeTravel.Type.SNAPSHOT_TIMESTAMP; }
+ valueExpr = Expression()
+ )
+ {
+ TimeTravelSpec timeTravel = new TimeTravelSpec(valueExpr, timeTravelType);
+ timeTravel.setSourceLocation(getSourceLocation(startToken));
+ return timeTravel;
+ }
+}
+
JoinClauseRightInput JoinClauseRightInput() throws ParseException :
{
Expression rightExpr = null;
VariableExpr rightVar = null;
VariableExpr posVar = null;
- String snapshotOrTimestamp = null;
- TimeTravel.Type timeTravelType = TimeTravel.Type.SNAPSHOT_ID;
+ TimeTravelSpec timeTravel = null;
}
{
rightExpr = Expression()
@@ -6394,29 +6398,14 @@ JoinClauseRightInput JoinClauseRightInput() throws ParseException :
(
<AT>
(
- LOOKAHEAD( { laIdentifier(SNAPSHOT) } )
- <IDENTIFIER> snapshotOrTimestamp = StringLiteral()
- { timeTravelType = TimeTravel.Type.SNAPSHOT_ID; }
- (
- <AT> posVar = Variable()
- )?
- | LOOKAHEAD( { laIdentifier(TIMESTAMP) } )
- <IDENTIFIER> snapshotOrTimestamp = StringLiteral()
- { timeTravelType = TimeTravel.Type.SNAPSHOT_TIMESTAMP; }
+ LOOKAHEAD( { laTimeTravelKeyword() } )
+ timeTravel = TimeTravelSpec()
(
<AT> posVar = Variable()
)?
| posVar = Variable()
(
- <AT>
- (
- LOOKAHEAD( { laIdentifier(SNAPSHOT) } )
- <IDENTIFIER> snapshotOrTimestamp = StringLiteral()
- { timeTravelType = TimeTravel.Type.SNAPSHOT_ID; }
- | LOOKAHEAD( { laIdentifier(TIMESTAMP) } )
- <IDENTIFIER> snapshotOrTimestamp = StringLiteral()
- { timeTravelType = TimeTravel.Type.SNAPSHOT_TIMESTAMP; }
- )
+ <AT> timeTravel = TimeTravelSpec()
)?
)
)?
@@ -6424,10 +6413,6 @@ JoinClauseRightInput JoinClauseRightInput() throws ParseException :
if (rightVar == null) {
rightVar = ExpressionToVariableUtil.getGeneratedVariable(rightExpr, true);
}
- TimeTravel timeTravel = null;
- if (snapshotOrTimestamp != null) {
- timeTravel = new TimeTravel(snapshotOrTimestamp, timeTravelType);
- }
return new JoinClauseRightInput(rightExpr, rightVar, posVar, timeTravel);
}
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/test/java/org/apache/asterix/lang/sqlpp/parser/TimeTravelParserTest.java b/asterixdb/asterix-lang-sqlpp/src/test/java/org/apache/asterix/lang/sqlpp/parser/TimeTravelParserTest.java
new file mode 100644
index 0000000000..f6114b858a
--- /dev/null
+++ b/asterixdb/asterix-lang-sqlpp/src/test/java/org/apache/asterix/lang/sqlpp/parser/TimeTravelParserTest.java
@@ -0,0 +1,295 @@
+/*
+ * 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.asterix.lang.sqlpp.parser;
+
+import java.util.List;
+
+import org.apache.asterix.common.exceptions.CompilationException;
+import org.apache.asterix.common.metadata.NamespaceResolver;
+import org.apache.asterix.lang.common.base.Expression;
+import org.apache.asterix.lang.common.base.IParser;
+import org.apache.asterix.lang.common.base.IParserFactory;
+import org.apache.asterix.lang.common.base.Literal;
+import org.apache.asterix.lang.common.base.Statement;
+import org.apache.asterix.lang.common.expression.LiteralExpr;
+import org.apache.asterix.lang.common.statement.Query;
+import org.apache.asterix.lang.sqlpp.clause.FromTerm;
+import org.apache.asterix.lang.sqlpp.clause.JoinClause;
+import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
+import org.apache.asterix.lang.sqlpp.struct.TimeTravelSpec;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Grammar coverage for the {@code AT SNAPSHOT} / {@code AT TIMESTAMP} value, which accepts an arbitrary
+ * expression rather than only a string literal. Whether the expression can actually be folded to a constant is
+ * decided later, during expression-to-plan translation, so this only pins down what parses and into what.
+ */
+public class TimeTravelParserTest {
+
+ @Test
+ public void testStringLiteralSnapshot() throws Exception {
+ TimeTravelSpec spec = fromTermTimeTravel("SELECT * FROM c AT SNAPSHOT \"8574821\";");
+ Assert.assertEquals(TimeTravel.Type.SNAPSHOT_ID, spec.getType());
+ Assert.assertEquals("8574821", stringLiteral(spec));
+ }
+
+ /**
+ * A bare integer used to be a parse error, which made the natural spelling of a snapshot id unusable.
+ */
+ @Test
+ public void testIntegerLiteralSnapshot() throws Exception {
+ TimeTravelSpec spec = fromTermTimeTravel("SELECT * FROM c AT SNAPSHOT 8574821;");
+ Assert.assertEquals(TimeTravel.Type.SNAPSHOT_ID, spec.getType());
+ Assert.assertEquals(Expression.Kind.LITERAL_EXPRESSION, spec.getValueExpression().getKind());
+ Assert.assertEquals(Literal.Type.LONG, ((LiteralExpr) spec.getValueExpression()).getValue().getLiteralType());
+ }
+
+ @Test
+ public void testTimestampKeyword() throws Exception {
+ TimeTravelSpec spec = fromTermTimeTravel("SELECT * FROM c AT TIMESTAMP \"2026-08-20\";");
+ Assert.assertEquals(TimeTravel.Type.SNAPSHOT_TIMESTAMP, spec.getType());
+ Assert.assertEquals("2026-08-20", stringLiteral(spec));
+ }
+
+ @Test
+ public void testNamedParameter() throws Exception {
+ TimeTravelSpec spec = fromTermTimeTravel("SELECT * FROM c AT TIMESTAMP $ts;");
+ Assert.assertEquals(Expression.Kind.VARIABLE_EXPRESSION, spec.getValueExpression().getKind());
+ }
+
+ @Test
+ public void testPositionalParameter() throws Exception {
+ Assert.assertEquals(Expression.Kind.VARIABLE_EXPRESSION,
+ fromTermTimeTravel("SELECT * FROM c AT SNAPSHOT $1;").getValueExpression().getKind());
+ Assert.assertEquals(Expression.Kind.VARIABLE_EXPRESSION,
+ fromTermTimeTravel("SELECT * FROM c AT SNAPSHOT ?;").getValueExpression().getKind());
+ }
+
+ @Test
+ public void testDateArithmetic() throws Exception {
+ TimeTravelSpec spec = fromTermTimeTravel("SELECT * FROM c AT TIMESTAMP current_date() - duration(\"P1D\");");
+ Assert.assertEquals(TimeTravel.Type.SNAPSHOT_TIMESTAMP, spec.getType());
+ Assert.assertEquals(Expression.Kind.OP_EXPRESSION, spec.getValueExpression().getKind());
+ }
+
+ @Test
+ public void testFunctionCall() throws Exception {
+ TimeTravelSpec spec =
+ fromTermTimeTravel("SELECT * FROM c AT TIMESTAMP unix_time_from_date_in_ms(date(\"2026-08-20\"));");
+ Assert.assertEquals(Expression.Kind.CALL_EXPRESSION, spec.getValueExpression().getKind());
+ }
+
+ /**
+ * {@code AT} is overloaded: it introduces both the time travel value and the positional variable, in either
+ * order.
+ */
+ @Test
+ public void testTimeTravelThenPositionalVariable() throws Exception {
+ FromTerm fromTerm = fromTerm("SELECT * FROM c AT SNAPSHOT 1 AT p;");
+ Assert.assertTrue(fromTerm.hasTimeTravel());
+ Assert.assertTrue(fromTerm.hasPositionalVariable());
+ }
+
+ @Test
+ public void testPositionalVariableThenTimeTravel() throws Exception {
+ FromTerm fromTerm = fromTerm("SELECT * FROM c AT p AT SNAPSHOT 1;");
+ Assert.assertTrue(fromTerm.hasTimeTravel());
+ Assert.assertTrue(fromTerm.hasPositionalVariable());
+ }
+
+ @Test
+ public void testPositionalVariableOnly() throws Exception {
+ FromTerm fromTerm = fromTerm("SELECT * FROM c AT p;");
+ Assert.assertFalse(fromTerm.hasTimeTravel());
+ Assert.assertTrue(fromTerm.hasPositionalVariable());
+ }
+
+ /**
+ * The value is a full expression now, so the parser has to stop at the {@code AT} that introduces the
+ * positional variable instead of trying to continue the expression. The bare-literal cases above cannot
+ * detect that, because a literal ends where an expression could not have continued anyway.
+ */
+ @Test
+ public void testTimeTravelExpressionThenPositionalVariable() throws Exception {
+ FromTerm fromTerm = fromTerm("SELECT * FROM c AT SNAPSHOT 5 - 4 AT p;");
+ Assert.assertEquals(Expression.Kind.OP_EXPRESSION, fromTerm.getTimeTravel().getValueExpression().getKind());
+ Assert.assertTrue(fromTerm.hasPositionalVariable());
+ }
+
+ @Test
+ public void testPositionalVariableThenTimeTravelExpression() throws Exception {
+ FromTerm fromTerm = fromTerm("SELECT * FROM c AT p AT SNAPSHOT 5 - 4;");
+ Assert.assertEquals(Expression.Kind.OP_EXPRESSION, fromTerm.getTimeTravel().getValueExpression().getKind());
+ Assert.assertTrue(fromTerm.hasPositionalVariable());
+ }
+
+ /**
+ * Alias, value and positional variable together: the alias binds before either {@code AT}, so all three
+ * have to come out distinct.
+ */
+ @Test
+ public void testAliasTimeTravelAndPositionalVariable() throws Exception {
+ FromTerm fromTerm = fromTerm("SELECT * FROM c AS x AT SNAPSHOT 5 - 4 AT p;");
+ Assert.assertEquals(Expression.Kind.OP_EXPRESSION, fromTerm.getTimeTravel().getValueExpression().getKind());
+ Assert.assertTrue(fromTerm.hasPositionalVariable());
+ Assert.assertNotEquals(fromTerm.getLeftVariable().getVar(), fromTerm.getPositionalVariable().getVar());
+ }
+
+ /**
+ * The JOIN clause repeats the same overloaded {@code AT} structure, from a separate grammar production.
+ */
+ @Test
+ public void testJoinTimeTravelAndPositionalVariable() throws Exception {
+ FromTerm fromTerm = fromTerm("SELECT * FROM c JOIN d AS y AT SNAPSHOT 5 - 4 AT q ON c.id = d.id;");
+ JoinClause joinClause = (JoinClause) fromTerm.getCorrelateClauses().get(0);
+ Assert.assertEquals(Expression.Kind.OP_EXPRESSION, joinClause.getTimeTravel().getValueExpression().getKind());
+ Assert.assertTrue(joinClause.hasPositionalVariable());
+ }
+
+ /**
+ * {@code SNAPSHOT} and {@code TIMESTAMP} are not reserved words, but the lookahead that selects the time
+ * travel branch keys on them, so neither can name a positional variable. This predates widening the value
+ * to an expression; it is pinned here so it stays a decision rather than becoming a surprise.
+ */
+ @Test
+ public void testTimeTravelKeywordCannotNamePositionalVariable() throws Exception {
+ assertParseError("SELECT * FROM c AT snapshot;");
+ assertParseError("SELECT * FROM c AT timestamp;");
+ }
+
+ @Test
+ public void testJoinClause() throws Exception {
+ FromTerm fromTerm = fromTerm("SELECT * FROM c JOIN d AT SNAPSHOT 5 - 4 ON c.id = d.id;");
+ JoinClause joinClause = (JoinClause) fromTerm.getCorrelateClauses().get(0);
+ Assert.assertTrue(joinClause.hasTimeTravel());
+ Assert.assertEquals(Expression.Kind.OP_EXPRESSION, joinClause.getTimeTravel().getValueExpression().getKind());
+ }
+
+ @Test
+ public void testMissingValueIsSyntaxError() throws Exception {
+ assertParseError("SELECT * FROM c AT SNAPSHOT;");
+ assertParseError("SELECT * FROM c AT TIMESTAMP;");
+ }
+
+ @Test
+ public void testArithmeticOnSnapshotId() throws Exception {
+ Assert.assertEquals(Expression.Kind.OP_EXPRESSION,
+ fromTermTimeTravel("SELECT * FROM c AT SNAPSHOT 8574821 + 0;").getValueExpression().getKind());
+ }
+
+ /**
+ * The value is an expression, so a parenthesised one parses like any other.
+ */
+ @Test
+ public void testParenthesized() throws Exception {
+ Assert.assertEquals(TimeTravel.Type.SNAPSHOT_ID,
+ fromTermTimeTravel("SELECT * FROM c AT SNAPSHOT (8574821);").getType());
+ }
+
+ @Test
+ public void testCrossJoin() throws Exception {
+ FromTerm fromTerm = fromTerm("SELECT * FROM c CROSS JOIN d AT SNAPSHOT 5;");
+ JoinClause joinClause = (JoinClause) fromTerm.getCorrelateClauses().get(0);
+ Assert.assertTrue(joinClause.hasTimeTravel());
+ }
+
+ @Test
+ public void testLeftOuterJoin() throws Exception {
+ FromTerm fromTerm = fromTerm("SELECT * FROM c LEFT OUTER JOIN d AT SNAPSHOT 5 ON c.id = d.id;");
+ Assert.assertTrue(((JoinClause) fromTerm.getCorrelateClauses().get(0)).hasTimeTravel());
+ }
+
+ @Test
+ public void testRightOuterJoin() throws Exception {
+ FromTerm fromTerm = fromTerm("SELECT * FROM c RIGHT OUTER JOIN d AT SNAPSHOT 5 ON c.id = d.id;");
+ Assert.assertTrue(((JoinClause) fromTerm.getCorrelateClauses().get(0)).hasTimeTravel());
+ }
+
+ /**
+ * UNNEST shares the correlate-clause base with JOIN but the grammar never attaches a value
+ * to it, so this must remain a syntax error rather than quietly parsing.
+ */
+ @Test
+ public void testUnnestTakesNoTimeTravel() throws Exception {
+ assertParseError("SELECT * FROM c UNNEST c.items AS i AT SNAPSHOT 5;");
+ }
+
+ @Test
+ public void testBothClausesInOneQuery() throws Exception {
+ FromTerm fromTerm = fromTerm("SELECT * FROM c AT SNAPSHOT 1 JOIN d AT SNAPSHOT 2 ON c.id = d.id;");
+ Assert.assertTrue(fromTerm.hasTimeTravel());
+ Assert.assertTrue(((JoinClause) fromTerm.getCorrelateClauses().get(0)).hasTimeTravel());
+ }
+
+ /**
+ * The visitors read the value without a null check, so a specification must never carry a null one.
+ */
+ @Test
+ public void testSpecRejectsNullValueAndType() throws Exception {
+ TimeTravelSpec spec = fromTermTimeTravel("SELECT * FROM c AT SNAPSHOT 1;");
+ assertNullRejected(() -> new TimeTravelSpec(null, TimeTravel.Type.SNAPSHOT_ID));
+ assertNullRejected(() -> new TimeTravelSpec(spec.getValueExpression(), null));
+ assertNullRejected(() -> spec.setValueExpression(null));
+ }
+
+ private static void assertNullRejected(Runnable r) {
+ try {
+ r.run();
+ Assert.fail("expected a null to be rejected");
+ } catch (NullPointerException expected) {
+ // the check this test exists for
+ }
+ }
+
+ private static String stringLiteral(TimeTravelSpec spec) {
+ Expression valueExpr = spec.getValueExpression();
+ Assert.assertEquals(Expression.Kind.LITERAL_EXPRESSION, valueExpr.getKind());
+ return ((LiteralExpr) valueExpr).getValue().getStringValue();
+ }
+
+ private static TimeTravelSpec fromTermTimeTravel(String query) throws Exception {
+ FromTerm fromTerm = fromTerm(query);
+ Assert.assertTrue("no time travel was parsed", fromTerm.hasTimeTravel());
+ return fromTerm.getTimeTravel();
+ }
+
+ private static FromTerm fromTerm(String query) throws Exception {
+ List<Statement> statements = parse(query);
+ Assert.assertEquals(1, statements.size());
+ SelectExpression selectExpr = (SelectExpression) ((Query) statements.get(0)).getBody();
+ return selectExpr.getSelectSetOperation().getLeftInput().getSelectBlock().getFromClause().getFromTerms().get(0);
+ }
+
+ private static List<Statement> parse(String query) throws CompilationException {
+ IParserFactory factory = new SqlppParserFactory(new NamespaceResolver(false));
+ IParser parser = factory.createParser(query);
+ return parser.parse();
+ }
+
+ private static void assertParseError(String query) {
+ try {
+ parse(query);
+ Assert.fail("expected a syntax error for: " + query);
+ } catch (CompilationException e) {
+ Assert.assertTrue(e.getMessage(), e.getMessage().contains("Syntax error"));
+ }
+ }
+}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
index f29ebce811..0131e69758 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
@@ -174,6 +174,7 @@ public class BuiltinFunctions {
private static final Map<FunctionIdentifier, FunctionIdentifier> aggregateToIntermediateAggregate = new HashMap<>();
private static final Map<FunctionIdentifier, FunctionIdentifier> aggregateToGlobalAggregate = new HashMap<>();
private static final Map<FunctionIdentifier, FunctionIdentifier> aggregateToSerializableAggregate = new HashMap<>();
+ private static final Map<FunctionIdentifier, FunctionIdentifier> jobClockToImmediateVariant = new HashMap<>();
private static final Map<FunctionIdentifier, FunctionIdentifier> scalarToAggregateFunctionMap = new HashMap<>();
private static final Map<FunctionIdentifier, FunctionIdentifier> distinctToRegularAggregateFunctionMap =
new HashMap<>();
@@ -2284,6 +2285,9 @@ public class BuiltinFunctions {
addFunction(CURRENT_TIME_IMMEDIATE, ATimeTypeComputer.INSTANCE, false);
addFunction(CURRENT_DATETIME, ADateTimeTypeComputer.INSTANCE, false);
addFunction(CURRENT_DATETIME_IMMEDIATE, ADateTimeTypeComputer.INSTANCE, false);
+ addImmediateVariant(CURRENT_DATE, CURRENT_DATE_IMMEDIATE);
+ addImmediateVariant(CURRENT_TIME, CURRENT_TIME_IMMEDIATE);
+ addImmediateVariant(CURRENT_DATETIME, CURRENT_DATETIME_IMMEDIATE);
addPrivateFunction(DAY_TIME_DURATION_GREATER_THAN, ABooleanTypeComputer.INSTANCE, true);
addPrivateFunction(DAY_TIME_DURATION_LESS_THAN, ABooleanTypeComputer.INSTANCE, true);
addPrivateFunction(YEAR_MONTH_DURATION_GREATER_THAN, ABooleanTypeComputer.INSTANCE, true);
@@ -3312,6 +3316,24 @@ public class BuiltinFunctions {
globalAggregateFunctions.add(globalfi);
}
+ /**
+ * Pairs a function that reads the job start time with the variant that reads the wall clock instead. A job
+ * clock function can only be evaluated inside a running job; where a value has to be computed without one
+ * (an {@code AT SNAPSHOT} / {@code AT TIMESTAMP} value is resolved while the query is compiled), the
+ * immediate variant is what can be used, and this is what the error naming it is built from.
+ */
+ public static void addImmediateVariant(FunctionIdentifier jobClockFi, FunctionIdentifier immediateFi) {
+ jobClockToImmediateVariant.put(jobClockFi, immediateFi);
+ }
+
+ /**
+ * @return the wall clock variant registered for a job clock function, or {@code null} if {@code fi} is not
+ * a job clock function
+ */
+ public static FunctionIdentifier getImmediateVariant(FunctionIdentifier fi) {
+ return jobClockToImmediateVariant.get(fi);
+ }
+
public static void addUnnestFun(FunctionIdentifier fi, boolean returnsUniqueValues) {
builtinUnnestingFunctions.put(fi, returnsUniqueValues);
}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/AbstractCurrentTemporalValueEval.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/AbstractCurrentTemporalValueEval.java
index e8c2707958..2ff068e922 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/AbstractCurrentTemporalValueEval.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/AbstractCurrentTemporalValueEval.java
@@ -21,13 +21,10 @@ package org.apache.asterix.runtime.evaluators.functions.temporal;
import java.io.DataOutput;
import java.time.DateTimeException;
-import java.time.Instant;
import java.time.ZoneId;
-import java.time.ZoneOffset;
import java.time.zone.ZoneRules;
-import java.util.concurrent.TimeUnit;
-import org.apache.asterix.om.base.temporal.GregorianCalendarSystem;
+import org.apache.asterix.om.types.ATypeTag;
import org.apache.asterix.runtime.evaluators.functions.AbstractScalarEval;
import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
import org.apache.hyracks.api.context.IEvaluatorContext;
@@ -42,7 +39,6 @@ abstract class AbstractCurrentTemporalValueEval extends AbstractScalarEval {
protected final IEvaluatorContext ctx;
protected final ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage();
protected final DataOutput out = resultStorage.getDataOutput();
- protected final GregorianCalendarSystem cal = GregorianCalendarSystem.getInstance();
private long jobStartTime = Long.MIN_VALUE;
private ZoneId jobStartTimeZoneId;
@@ -53,12 +49,15 @@ abstract class AbstractCurrentTemporalValueEval extends AbstractScalarEval {
this.ctx = ctx;
}
- protected final long getSystemCurrentTimeAsAdjustedChronon() throws HyracksDataException {
- return getChrononAdjusted(System.currentTimeMillis());
+ protected final long getSystemCurrentTimeValue(ATypeTag type) throws HyracksDataException {
+ ensureJobStartTimeZone();
+ return CurrentTemporalValueUtil.valueAt(type, System.currentTimeMillis(), jobStartTimeZoneRules);
}
- protected final long getJobStartTimeAsAdjustedChronon() throws HyracksDataException {
- return getChrononAdjusted(getJobStartTime());
+ protected final long getJobStartTimeValue(ATypeTag type) throws HyracksDataException {
+ long startTime = getJobStartTime();
+ ensureJobStartTimeZone();
+ return CurrentTemporalValueUtil.valueAt(type, startTime, jobStartTimeZoneRules);
}
private long getJobStartTime() throws HyracksDataException {
@@ -86,10 +85,4 @@ abstract class AbstractCurrentTemporalValueEval extends AbstractScalarEval {
}
}
}
-
- private long getChrononAdjusted(long chronon) throws HyracksDataException {
- ensureJobStartTimeZone();
- ZoneOffset tzOffset = jobStartTimeZoneRules.getOffset(Instant.ofEpochMilli(chronon));
- return cal.adjustChrononByTimezone(chronon, (int) -TimeUnit.SECONDS.toMillis(tzOffset.getTotalSeconds()));
- }
}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateEval.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateEval.java
index d8e9b594d4..8b008e532d 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateEval.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateEval.java
@@ -19,6 +19,7 @@
package org.apache.asterix.runtime.evaluators.functions.temporal;
+import org.apache.asterix.om.types.ATypeTag;
import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
import org.apache.hyracks.api.context.IEvaluatorContext;
import org.apache.hyracks.api.exceptions.HyracksDataException;
@@ -35,7 +36,7 @@ class CurrentDateEval extends AbstractCurrentDateEval {
@Override
protected int getCurrentDateChrononAdjusted() throws HyracksDataException {
if (dateChrononAdjusted == Integer.MIN_VALUE) {
- dateChrononAdjusted = cal.getChrononInDays(getJobStartTimeAsAdjustedChronon());
+ dateChrononAdjusted = (int) getJobStartTimeValue(ATypeTag.DATE);
}
return dateChrononAdjusted;
}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateImmediateEval.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateImmediateEval.java
index 1869da195b..53ecc3ab67 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateImmediateEval.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateImmediateEval.java
@@ -19,6 +19,7 @@
package org.apache.asterix.runtime.evaluators.functions.temporal;
+import org.apache.asterix.om.types.ATypeTag;
import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
import org.apache.hyracks.api.context.IEvaluatorContext;
import org.apache.hyracks.api.exceptions.HyracksDataException;
@@ -32,6 +33,6 @@ class CurrentDateImmediateEval extends AbstractCurrentDateEval {
@Override
protected int getCurrentDateChrononAdjusted() throws HyracksDataException {
- return cal.getChrononInDays(getSystemCurrentTimeAsAdjustedChronon());
+ return (int) getSystemCurrentTimeValue(ATypeTag.DATE);
}
}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeEval.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeEval.java
index 45d1cdab6a..711947dd9a 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeEval.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeEval.java
@@ -19,6 +19,7 @@
package org.apache.asterix.runtime.evaluators.functions.temporal;
+import org.apache.asterix.om.types.ATypeTag;
import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
import org.apache.hyracks.api.context.IEvaluatorContext;
import org.apache.hyracks.api.exceptions.HyracksDataException;
@@ -35,7 +36,7 @@ class CurrentDateTimeEval extends AbstractCurrentDateTimeEval {
@Override
protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
if (chrononAdjusted == Long.MIN_VALUE) {
- chrononAdjusted = getJobStartTimeAsAdjustedChronon();
+ chrononAdjusted = getJobStartTimeValue(ATypeTag.DATETIME);
}
return chrononAdjusted;
}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
index 552c9f4af7..4e36b422c4 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentDateTimeImmediateEval.java
@@ -19,6 +19,7 @@
package org.apache.asterix.runtime.evaluators.functions.temporal;
+import org.apache.asterix.om.types.ATypeTag;
import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
import org.apache.hyracks.api.context.IEvaluatorContext;
import org.apache.hyracks.api.exceptions.HyracksDataException;
@@ -31,6 +32,6 @@ class CurrentDateTimeImmediateEval extends AbstractCurrentDateTimeEval {
@Override
protected long getCurrentDateTimeChrononAdjusted() throws HyracksDataException {
- return getSystemCurrentTimeAsAdjustedChronon();
+ return getSystemCurrentTimeValue(ATypeTag.DATETIME);
}
}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentTemporalValueUtil.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentTemporalValueUtil.java
new file mode 100644
index 0000000000..e5a47d7c53
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentTemporalValueUtil.java
@@ -0,0 +1,60 @@
+/*
+ * 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.asterix.runtime.evaluators.functions.temporal;
+
+import java.time.Instant;
+import java.time.zone.ZoneRules;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.asterix.om.base.temporal.GregorianCalendarSystem;
+import org.apache.asterix.om.types.ATypeTag;
+
+public final class CurrentTemporalValueUtil {
+
+ private static final GregorianCalendarSystem CAL = GregorianCalendarSystem.getInstance();
+
+ private CurrentTemporalValueUtil() {
+ }
+
+ /**
+ * The value a current date, time or datetime function produces for an instant read in a zone.
+ *
+ * @param type {@link ATypeTag#DATE}, {@link ATypeTag#TIME} or {@link ATypeTag#DATETIME}
+ * @param epochMillis the instant
+ * @param zoneRules the zone the instant is read in
+ * @return the date chronon (days since the epoch), the time chronon (milliseconds into the day) or the
+ * datetime chronon
+ */
+ public static long valueAt(ATypeTag type, long epochMillis, ZoneRules zoneRules) {
+ int offsetMillis = (int) TimeUnit.SECONDS
+ .toMillis(zoneRules.getOffset(Instant.ofEpochMilli(epochMillis)).getTotalSeconds());
+ long local = CAL.adjustChrononByTimezone(epochMillis, -offsetMillis);
+ switch (type) {
+ case DATE:
+ return CAL.getChrononInDays(local);
+ case TIME:
+ return CAL.getTimeChronon(local);
+ case DATETIME:
+ return local;
+ default:
+ throw new IllegalArgumentException(type.toString());
+ }
+ }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentTimeEval.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentTimeEval.java
index 419ddd77c5..2ca3227ff6 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentTimeEval.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentTimeEval.java
@@ -19,6 +19,7 @@
package org.apache.asterix.runtime.evaluators.functions.temporal;
+import org.apache.asterix.om.types.ATypeTag;
import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
import org.apache.hyracks.api.context.IEvaluatorContext;
import org.apache.hyracks.api.exceptions.HyracksDataException;
@@ -35,7 +36,7 @@ class CurrentTimeEval extends AbstractCurrentTimeEval {
@Override
protected int getCurrentTimeChrononAdjusted() throws HyracksDataException {
if (timeChrononAdjusted == Integer.MIN_VALUE) {
- timeChrononAdjusted = cal.getTimeChronon(getJobStartTimeAsAdjustedChronon());
+ timeChrononAdjusted = (int) getJobStartTimeValue(ATypeTag.TIME);
}
return timeChrononAdjusted;
}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentTimeImmediateEval.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentTimeImmediateEval.java
index 577b0f72c8..695ab61bb3 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentTimeImmediateEval.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/temporal/CurrentTimeImmediateEval.java
@@ -19,6 +19,7 @@
package org.apache.asterix.runtime.evaluators.functions.temporal;
+import org.apache.asterix.om.types.ATypeTag;
import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
import org.apache.hyracks.api.context.IEvaluatorContext;
import org.apache.hyracks.api.exceptions.HyracksDataException;
@@ -32,6 +33,6 @@ class CurrentTimeImmediateEval extends AbstractCurrentTimeEval {
@Override
protected int getCurrentTimeChrononAdjusted() throws HyracksDataException {
- return cal.getTimeChronon(getSystemCurrentTimeAsAdjustedChronon());
+ return (int) getSystemCurrentTimeValue(ATypeTag.TIME);
}
}
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IOptimizationContext.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IOptimizationContext.java
index 78cbf7f0c5..97f70a63ae 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IOptimizationContext.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IOptimizationContext.java
@@ -107,4 +107,15 @@ public interface IOptimizationContext extends ITypingContext, IVariableContext {
IndexAdvisor getIndexAdvisor();
+ /**
+ * Records that the plan being optimized holds a value computed during optimization that is only valid for
+ * this compilation, such as a clock read, so the plan must not be reused for a later run of the statement.
+ */
+ void markPlanNotReusable();
+
+ /**
+ * @return {@code false} if {@link #markPlanNotReusable()} was called for this plan
+ */
+ boolean isPlanReusable();
+
}
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractUnnestNonMapOperator.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractUnnestNonMapOperator.java
index fed8a5368d..531c4b2161 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractUnnestNonMapOperator.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractUnnestNonMapOperator.java
@@ -23,10 +23,12 @@ import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
import org.apache.hyracks.algebricks.core.algebra.properties.FilteredVariablePropagationPolicy;
import org.apache.hyracks.algebricks.core.algebra.properties.VariablePropagationPolicy;
+import org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalExpressionReferenceTransform;
public abstract class AbstractUnnestNonMapOperator extends AbstractUnnestOperator {
@@ -76,6 +78,20 @@ public abstract class AbstractUnnestNonMapOperator extends AbstractUnnestOperato
this.positionalVariableType = positionalVariableType;
}
+ /**
+ * Also transforms the time travel value, so that it is folded, type-checked and printed by the same rules
+ * that handle every other expression an operator carries -- constant folding in particular, which is what
+ * reduces it to the constant the scan needs.
+ */
+ @Override
+ public boolean acceptExpressionTransform(ILogicalExpressionReferenceTransform visitor) throws AlgebricksException {
+ boolean changed = super.acceptExpressionTransform(visitor);
+ if (timeTravel != null) {
+ changed |= visitor.transform(timeTravel.getValueExpressionRef());
+ }
+ return changed;
+ }
+
public Object getPositionalVariableType() {
return positionalVariableType;
}
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/TimeTravel.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/TimeTravel.java
index 52325fd861..310dd08969 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/TimeTravel.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/TimeTravel.java
@@ -21,6 +21,18 @@ package org.apache.hyracks.algebricks.core.algebra.operators.logical;
import java.util.Objects;
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+
+/**
+ * The {@code AT SNAPSHOT} / {@code AT TIMESTAMP} specification of a time travelling scan.
+ * <p>
+ * The value is an ordinary expression reference, exposed through
+ * {@link AbstractUnnestNonMapOperator#acceptExpressionTransform} like any other operator expression, so
+ * constant folding reduces it in the normal course of optimization. It has to end up a constant: the value
+ * selects which snapshot's manifest and data files the scan reads, so it must be known while the plan is still
+ * being built, and whoever consumes it (see {@code DatasetRewriter}) rejects anything that did not fold.
+ */
public class TimeTravel {
public enum Type {
@@ -38,16 +50,20 @@ public class TimeTravel {
}
}
- private final String snapshotIdOrTimestamp;
+ private final Mutable<ILogicalExpression> valueExpressionRef;
private final Type type;
- public TimeTravel(String snapshotIdOrTimestamp, Type type) {
- this.snapshotIdOrTimestamp = snapshotIdOrTimestamp;
+ public TimeTravel(Mutable<ILogicalExpression> valueExpressionRef, Type type) {
+ this.valueExpressionRef = valueExpressionRef;
this.type = type;
}
- public String getSnapshotIdOrTimestamp() {
- return snapshotIdOrTimestamp;
+ public Mutable<ILogicalExpression> getValueExpressionRef() {
+ return valueExpressionRef;
+ }
+
+ public ILogicalExpression getValueExpression() {
+ return valueExpressionRef.getValue();
}
public Type getType() {
@@ -62,17 +78,16 @@ public class TimeTravel {
if (!(object instanceof TimeTravel target)) {
return false;
}
- return Objects.equals(snapshotIdOrTimestamp, target.getSnapshotIdOrTimestamp())
- && Objects.equals(type, target.getType());
+ return type == target.type && Objects.equals(getValueExpression(), target.getValueExpression());
}
@Override
public int hashCode() {
- return Objects.hash(snapshotIdOrTimestamp, type);
+ return Objects.hash(getValueExpression(), type);
}
@Override
public String toString() {
- return type.getKeyword() + " " + snapshotIdOrTimestamp;
+ return type.getKeyword() + " " + getValueExpression();
}
}
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/LogicalOperatorDeepCopyWithNewVariablesVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/LogicalOperatorDeepCopyWithNewVariablesVisitor.java
index 186b9569fb..a4e87ef320 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/LogicalOperatorDeepCopyWithNewVariablesVisitor.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/LogicalOperatorDeepCopyWithNewVariablesVisitor.java
@@ -70,6 +70,7 @@ import org.apache.hyracks.algebricks.core.algebra.operators.logical.SelectOperat
import org.apache.hyracks.algebricks.core.algebra.operators.logical.SplitOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.SubplanOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.SwitchOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.TokenizeOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnionAllOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestMapOperator;
@@ -605,7 +606,8 @@ public class LogicalOperatorDeepCopyWithNewVariablesVisitor
public ILogicalOperator visitUnnestOperator(UnnestOperator op, ILogicalOperator arg) throws AlgebricksException {
UnnestOperator opCopy = new UnnestOperator(deepCopyVariable(op.getVariable()),
exprDeepCopyVisitor.deepCopyExpressionReference(op.getExpressionRef()),
- deepCopyVariable(op.getPositionalVariable()), op.getPositionalVariableType(), op.getTimeTravel());
+ deepCopyVariable(op.getPositionalVariable()), op.getPositionalVariableType(),
+ deepCopyTimeTravel(op.getTimeTravel()));
deepCopyInputsAnnotationsAndExecutionMode(op, arg, opCopy);
return opCopy;
}
@@ -643,7 +645,7 @@ public class LogicalOperatorDeepCopyWithNewVariablesVisitor
LeftOuterUnnestOperator opCopy = new LeftOuterUnnestOperator(deepCopyVariable(op.getVariable()),
exprDeepCopyVisitor.deepCopyExpressionReference(op.getExpressionRef()),
deepCopyVariable(op.getPositionalVariable()), op.getPositionalVariableType(), op.getMissingValue(),
- op.getTimeTravel());
+ deepCopyTimeTravel(op.getTimeTravel()));
deepCopyInputsAnnotationsAndExecutionMode(op, arg, opCopy);
return opCopy;
}
@@ -685,4 +687,10 @@ public class LogicalOperatorDeepCopyWithNewVariablesVisitor
public LinkedHashMap<LogicalVariable, LogicalVariable> getInputToOutputVariableMapping() {
return inputVarToOutputVarMapping;
}
+
+ private TimeTravel deepCopyTimeTravel(TimeTravel timeTravel) throws AlgebricksException {
+ return timeTravel == null ? null
+ : new TimeTravel(exprDeepCopyVisitor.deepCopyExpressionReference(timeTravel.getValueExpressionRef()),
+ timeTravel.getType());
+ }
}
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/OperatorDeepCopyVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/OperatorDeepCopyVisitor.java
index 7ba1c20e50..1f46cd49a7 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/OperatorDeepCopyVisitor.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/OperatorDeepCopyVisitor.java
@@ -70,6 +70,7 @@ import org.apache.hyracks.algebricks.core.algebra.operators.logical.SinkOperator
import org.apache.hyracks.algebricks.core.algebra.operators.logical.SplitOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.SubplanOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.SwitchOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.TimeTravel;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.TokenizeOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnionAllOperator;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestMapOperator;
@@ -278,7 +279,7 @@ public class OperatorDeepCopyVisitor implements ILogicalOperatorVisitor<ILogical
@Override
public ILogicalOperator visitUnnestOperator(UnnestOperator op, Void arg) throws AlgebricksException {
return new UnnestOperator(op.getVariable(), deepCopyExpressionRef(op.getExpressionRef()),
- op.getPositionalVariable(), op.getPositionalVariableType(), op.getTimeTravel());
+ op.getPositionalVariable(), op.getPositionalVariableType(), deepCopyTimeTravel(op.getTimeTravel()));
}
@Override
@@ -469,7 +470,8 @@ public class OperatorDeepCopyVisitor implements ILogicalOperatorVisitor<ILogical
public ILogicalOperator visitLeftOuterUnnestOperator(LeftOuterUnnestOperator op, Void arg)
throws AlgebricksException {
return new LeftOuterUnnestOperator(op.getVariable(), deepCopyExpressionRef(op.getExpressionRef()),
- op.getPositionalVariable(), op.getPositionalVariableType(), op.getMissingValue(), op.getTimeTravel());
+ op.getPositionalVariable(), op.getPositionalVariableType(), op.getMissingValue(),
+ deepCopyTimeTravel(op.getTimeTravel()));
}
@Override
@@ -507,4 +509,9 @@ public class OperatorDeepCopyVisitor implements ILogicalOperatorVisitor<ILogical
}
return newWinOp;
}
+
+ private TimeTravel deepCopyTimeTravel(TimeTravel timeTravel) throws AlgebricksException {
+ return timeTravel == null ? null
+ : new TimeTravel(deepCopyExpressionRef(timeTravel.getValueExpressionRef()), timeTravel.getType());
+ }
}
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
index 17102f3afc..657f6a9612 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
@@ -406,7 +406,7 @@ public class LogicalOperatorPrettyPrintVisitor extends AbstractLogicalOperatorPr
if (op.hasTimeTravel()) {
TimeTravel timeTravel = op.getTimeTravel();
buffer.append(" at ").append(timeTravel.getType().getKeyword()).append(" ")
- .append(timeTravel.getSnapshotIdOrTimestamp());
+ .append(timeTravel.getValueExpression().accept(exprVisitor, indent));
}
if (op.hasPositionalVariable()) {
buffer.append(" at ").append(String.valueOf(op.getPositionalVariable()));
@@ -424,7 +424,7 @@ public class LogicalOperatorPrettyPrintVisitor extends AbstractLogicalOperatorPr
if (op.hasTimeTravel()) {
TimeTravel timeTravel = op.getTimeTravel();
buffer.append(" at ").append(timeTravel.getType().getKeyword()).append(" ")
- .append(timeTravel.getSnapshotIdOrTimestamp());
+ .append(timeTravel.getValueExpression().accept(exprVisitor, indent));
}
if (op.getPositionalVariable() != null) {
buffer.append(" at " + op.getPositionalVariable());
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/rewriter/base/AlgebricksOptimizationContext.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/rewriter/base/AlgebricksOptimizationContext.java
index ae9767867b..a38ca53654 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/rewriter/base/AlgebricksOptimizationContext.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/rewriter/base/AlgebricksOptimizationContext.java
@@ -83,6 +83,7 @@ public class AlgebricksOptimizationContext implements IOptimizationContext {
private IMetadataProvider metadataProvider;
private Object compilerFactory;
private IndexAdvisor indexAdvisor;
+ private boolean planReusable = true;
public AlgebricksOptimizationContext(IOptimizationContextFactory optContextFactory, int varCounter,
IExpressionEvalSizeComputer expressionEvalSizeComputer,
@@ -413,4 +414,14 @@ public class AlgebricksOptimizationContext implements IOptimizationContext {
return indexAdvisor;
}
+ @Override
+ public void markPlanNotReusable() {
+ planReusable = false;
+ }
+
+ @Override
+ public boolean isPlanReusable() {
+ return planReusable;
+ }
+
}
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/utils/LogicalOperatorDotVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/utils/LogicalOperatorDotVisitor.java
index 370c10f527..e3390c272c 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/utils/LogicalOperatorDotVisitor.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/utils/LogicalOperatorDotVisitor.java
@@ -392,7 +392,7 @@ public class LogicalOperatorDotVisitor implements ILogicalOperatorVisitor<String
stringBuilder.append("unnest ").append(op.getVariable());
if (op.getTimeTravel() != null) {
String keyword = op.getTimeTravel().getType().getKeyword();
- String timeTravelValue = op.getTimeTravel().getSnapshotIdOrTimestamp();
+ String timeTravelValue = String.valueOf(op.getTimeTravel().getValueExpression());
stringBuilder.append(" at ").append(keyword).append(" ").append(timeTravelValue);
}
if (op.getPositionalVariable() != null) {
@@ -415,7 +415,7 @@ public class LogicalOperatorDotVisitor implements ILogicalOperatorVisitor<String
stringBuilder.append("outer-unnest ").append(op.getVariable());
if (op.getTimeTravel() != null) {
String keyword = op.getTimeTravel().getType().getKeyword();
- String timeTravelValue = op.getTimeTravel().getSnapshotIdOrTimestamp();
+ String timeTravelValue = String.valueOf(op.getTimeTravel().getValueExpression());
stringBuilder.append(" at ").append(keyword).append(" ").append(timeTravelValue);
}
if (op.getPositionalVariable() != null) {