Skip to content

Implement day-time intervals and current timestamp value function - #4645

Open
hazefully wants to merge 15 commits into
FoundationDB:mainfrom
hazefully:add-current-time-function
Open

hazefully wants to merge 15 commits into
FoundationDB:mainfrom
hazefully:add-current-time-function

Conversation

@hazefully

@hazefully hazefully commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

This PR adds SQL support for the datetime value function CURRENT_TIMESTAMP
and for day-time INTERVAL literals as described in the SQL standard.

With the changes included in this PR, a following query would be possible:

SELECT * FROM T1 WHERE timestamp > CURRENT_TIMESTAMP() - INTERVAL '10' DAY

This PR doesn't add dedicated TIMESTAMP or INTERVAL data types yet, which is covered by #4738, so both SQL constructs underneath return long values:

  • The timestamp value returned by CURRENT_TIMESTAMP is the number of milliseconds since the Unix epoch as a bigint/Type.TypeCode.LONG.
  • Intervals are a signed number of milliseconds as a bigint/Type.TypeCode.LONG, which can be negative to indicate a negative interval.

The new CURRENT_TIMESTAMP built-in function and value

The new CURRENT_TIMESTAMP built-in function added in this PR evaluates to a new CurrentTimestampValue, which always evaluates to the evaluation timestamp stored in the query's EvalutionContext. This ensures that multiple invocations of theCURRENT_TIMESTAMP function in a single query return the same value, and that a continued query is continued with the same evaluation timestamp. The evaluation timestamp is derived from the system clock (truncated to milliseconds) while normalizing the query, unless the connection option FIXED_EPOCH_TIMESTAMP_MILLIS_FOR_CURRENT_DATETIME_FUNCTIONS is set with a fixed timestamp to use for all current datetime value functions.

The CURRENT_TIMESTAMP function can also be used in INSERT queries, and they will insert the same value for all records inserted in a single query.

Interval literals

Interval literals are parsed during query parsing time (which means that interval literals can't use prepared statement parameters such as INTERVAL ? DAY TO SECOND). The parsing is done according to SQL standard rules, as detailed here, and only day-time intervals (i.e. INTERVAL ... DAY TO SECOND) are currently supported. New SQL error codes are added to handle different cases of invalid interval literals: INVALID_INTERVAL_FORMAT (22006) and INTERVAL_FIELD_OVERFLOW (22015). See the tests added in ParseHelpersTest.java for some test-cases outlining how the parsing works.

Queries with day-time interval literals are canonicalized to SELECT .... INTERVAL ? DAY TO SECOND before the plans are stored in the plan cache, and the parsed interval literal string is stored as a scalar literal. See AstNormalizerTests.java for tests that confirm this normalization works as expected.

Supported operations involving interval literals and datetime value functions

It is possible to add or subtract an interval from the return value of a date-time value function as shown in the example above, the current operations are supported:

  • CURRENT_TIMESTAMP - | + INTERVAL
  • INTERVAL + CURRENT_TIMESTAMP

These rules are encoded in the grammar defined in RelationalParser.g4 as the new rule datetimeIntervalMathExpression.

Documentation

The SQL docs are intentionally not updated in this PR, and they will be updated once dedicated data types for TIMESTAMP and INTERVAL values exist, which is tracked in #4738.

@hazefully
hazefully force-pushed the add-current-time-function branch from f05301a to 9972f31 Compare October 12, 2026 11:41
@hazefully
hazefully force-pushed the add-current-time-function branch from 9972f31 to 8079704 Compare October 13, 2026 10:37
@hazefully hazefully added the enhancement New feature or request label Oct 13, 2026
@hazefully
hazefully force-pushed the add-current-time-function branch 2 times, most recently from 8df6ed4 to 85a8b03 Compare October 13, 2026 13:56
@hazefully hazefully changed the title Implement interval and CURRENT_TIMESTAMP datetime value function Implement day-time intervals and CURRENT_TIMESTAMP datetime value function Oct 13, 2026
@hazefully hazefully changed the title Implement day-time intervals and CURRENT_TIMESTAMP datetime value function Implement day-time intervals and current timestamp value function Oct 13, 2026
@hazefully
hazefully force-pushed the add-current-time-function branch 3 times, most recently from 79bf749 to d47daba Compare October 13, 2026 15:09
@hazefully
hazefully force-pushed the add-current-time-function branch from d47daba to d1b23d5 Compare October 13, 2026 15:11
@hazefully
hazefully requested a review from hatyo October 13, 2026 16:08
@hazefully
hazefully marked this pull request as ready for review October 13, 2026 16:10
class CurrentTimestampValueTest {

@Test
void correlationsAreEmpty() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests feel superfluous if I am honest. I am ok with removing these test file, up to you.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only had them because of test gaps reported by team scale, but they are no longer needed and I replaced them with some basic tests that actually check the behaviour of the value.

/**
* A fixed Unix epoch timestamp in milliseconds to use in current datetime value functions.
*/
FIXED_EPOCH_TIMESTAMP_MILLIS_FOR_CURRENT_DATETIME_FUNCTIONS

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is mainly used for testing purposes to make tests with time more deterministic, is my understanding correct?
If so, I wonder if there is a better way for doing this that does not necessitate adding a new option that could be (ab)used in prod.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is correct, I tried to think of other ways enable setting a fixed timestamp for testing other than options but I couldn't think of anything that is much better:

  1. We can add a new class TestOnlyOptions and introduce a new method RelationalConnection.setTestOnlyOption so at least it is more obvious if the option is used from test code.
  2. We can make this a special clause in the query (something like WITH FIXED TIMESTAMP <>), and add that to the QueryExecutionContext.

Or alternatively we can just remove this feature, if you think it doesn't buy us that much functionality (we should still be able to test everything we just won't be able to assert on the values of the timestamp column itself).

: aggregateWindowedFunction #aggregateFunctionCall // done (supported)
| nonAggregateWindowedFunction #nonAggregateFunctionCall // done
| specificFunction #specificFunctionCall //
| datetimeValueFunction #datetimeValueFunctionCall // done

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a specific reason for moving the #dataTypeFunctionCall branch in specificFunction rule to a specific rule here?

Both representations are ok, but I find the previous representation more concise honestly. The functionCall rule is trying to define a taxonomy of functions "types", dateTime[Value]Function is an instance of specificFunction that is analogous to case and other built-in functions.

UPDATE: I think I see it, this is probably because you're resing the datetimeValueFunction parse rule to define the left part of date-time functions below.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that was the original motivation, since we can't enforce the arithmetic rules via ArithmeticValue.PhysicalOperator since all the types are just long. I am also making use of it now since datetimeValueFunction needs to create a CurrentTimestampValue with the ConstantObjectValue that corresponds to the evaluation timestamp constant.

| functionNameKeyword
| ASCII | CURDATE | CURRENT_DATE | CURRENT_TIME
| CURRENT_TIMESTAMP | CURTIME | DATE_ADD | DATE_SUB
| ASCII | DATE_ADD | DATE_SUB

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is good that you removed CURRENT_TIMESTSAMP et al from here, I think this removes a source of ambiguity, (which was probably ok up until now, because we didn't support the removed functions anyway).

* A value that evaluates to the evaluation timestamp of the current query, as the number of milliseconds since the
* Unix epoch.
* <p>
* The timestamp is not read from the system clock by this value, instead it is retrieved from the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think having this value not reading the current time stamp directly from the machine is definitely the right call.

However, I do believe that we could have it read that particular timestamp constant from the constant box in the bindings instead of having a special binding. This means that the current timestamp will have a (constant) correlation that is provided by the execution context.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be addressed now as mentioned in #4645 (comment)

@Override
public Expression visitIntervalLiteral(final RelationalParser.IntervalLiteralContext ctx) {
final var intervalStringLiteralExpression =
getDelegate().getPlanGenerationContext().withDisabledLiteralProcessing(() -> visitStringLiteral(ctx.value));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope we get to streamline this logic once we introduce a dedicated timestamp and interval types. We can discuss this later.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify the logic here a bit we are parsing the interval here to surface a syntax error to the user while generating the logical plan, instead of having something like IntervalValue which does the parsing on evaluation while executing the plan. I don't think the logic here will change much even when we have dedicated types if we are happy with this approach.

public Expression visitIntervalLiteral(final RelationalParser.IntervalLiteralContext ctx) {
final var intervalStringLiteralExpression =
getDelegate().getPlanGenerationContext().withDisabledLiteralProcessing(() -> visitStringLiteral(ctx.value));
final var intervalStringLiteralValue =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the ctx can have either a value or prepared parameter, in either case, I think we should strip it and create a CoV out of it.
It doesn't seem that we're handling the prepared parameter case here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a mistake in the RelationalParser, it shouldn't support prepared parameters here because I went for eagerly parsing the passed interval literals to an actual value while generating the logical plan. We do strip the parsed interval and create a CoV out of that at the end, hence why we disable the literal processing when we parse the string literal.

private final PlanHashable.PlanHashMode planHashMode;

@Nonnull
private final Instant evaluationTimestamp;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like we're always setting the evaluation timestamp which would translate to adding 8-bytes to query plan continuations.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is true, and I tried to change this to make it only added when it is used, but this doesn't work for SQL table functions which might be making use of CURRENT_TIMESTAMP, since at the AstNormalizer level there is no way to know what user-defined table functions are being used and to import the evaluation constants that are referenced in them (I think this is why we add all literals referenced in all temporary functions in the transaction while normalising the query as well).

@hazefully
hazefully requested a review from hatyo October 23, 2026 14:13
@github-actions

Copy link
Copy Markdown

📊 Metrics Diff Analysis Report

Summary

  • New queries: 7
  • Dropped queries: 0
  • Plan changed + metrics changed: 0
  • Plan unchanged + metrics changed: 0
ℹ️ About this analysis

This automated analysis compares query planner metrics between the base branch and this PR. It categorizes changes into:

  • New queries: Queries added in this PR
  • Dropped queries: Queries removed in this PR. These should be reviewed to ensure we are not losing coverage.
  • Plan changed + metrics changed: The query plan has changed along with planner metrics.
  • Metrics only changed: Same plan but different metrics

The last category in particular may indicate planner regressions that should be investigated.

New Queries

Count of new queries by file:

  • yaml-tests/src/test/resources/datetime-value-functions.metrics.yaml: 7

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants