Skip to content

Completed 100% temporal OpenCypher TCK#3369

Merged
lvca merged 22 commits intomainfrom
opencypher-tck
Feb 8, 2026
Merged

Completed 100% temporal OpenCypher TCK#3369
lvca merged 22 commits intomainfrom
opencypher-tck

Conversation

@lvca
Copy link
Contributor

@lvca lvca commented Feb 8, 2026

No description provided.

lvca added 21 commits February 6, 2026 10:24
Reached much better compliance with the latest changes:

```
=======================================================
         OpenCypher TCK Compliance Report
=======================================================
Total scenarios:  3897
Passed:           1908 (48%)
Failed:           1939 (49%)
Skipped:          50 (1%)
-------------------------------------------------------
By category:
  clauses/call                               2/ 52 passed (3%)
  clauses/create                            64/ 78 passed (82%)
  clauses/delete                            24/ 41 passed (58%)
  clauses/match                            292/381 passed (76%)
  clauses/match-where                       25/ 34 passed (73%)
  clauses/merge                             47/ 75 passed (62%)
  clauses/remove                            29/ 33 passed (87%)
  clauses/return                            35/ 63 passed (55%)
  clauses/return-orderby                    23/ 35 passed (65%)
  clauses/return-skip-limit                 26/ 31 passed (83%)
  clauses/set                               30/ 53 passed (56%)
  clauses/union                              8/ 12 passed (66%)
  clauses/unwind                            10/ 14 passed (71%)
  clauses/with                              14/ 29 passed (48%)
  clauses/with-orderBy                     124/292 passed (42%)
  clauses/with-skip-limit                    7/  9 passed (77%)
  clauses/with-where                        10/ 19 passed (52%)
  expressions/aggregation                   23/ 35 passed (65%)
  expressions/boolean                      150/150 passed (100%)
  expressions/comparison                    36/ 72 passed (50%)
  expressions/conditional                   13/ 13 passed (100%)
  expressions/existentialSubqueries          4/ 10 passed (40%)
  expressions/graph                         32/ 61 passed (52%)
  expressions/list                         120/185 passed (64%)
  expressions/literals                     120/131 passed (91%)
  expressions/map                           28/ 44 passed (63%)
  expressions/mathematical                   3/  6 passed (50%)
  expressions/null                          44/ 44 passed (100%)
  expressions/path                           0/  7 passed (0%)
  expressions/pattern                       19/ 50 passed (38%)
  expressions/precedence                    20/121 passed (16%)
  expressions/quantifier                   478/604 passed (79%)
  expressions/string                        22/ 32 passed (68%)
  expressions/temporal                       0/1004 passed (0%)
  expressions/typeConversion                19/ 47 passed (40%)
  useCases/countingSubgraphMatches           6/ 11 passed (54%)
  useCases/triadicSelection                  1/ 19 passed (5%)
=======================================================
```
@lvca lvca self-assigned this Feb 8, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @lvca, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly advances the OpenCypher temporal functionality, bringing it closer to 100% TCK completion. It addresses several critical aspects of temporal data handling, including precise arithmetic with durations, correct parsing of complex expressions, and robust duration normalization. Additionally, it improves the query optimizer's decision-making for unsupported MATCH patterns and updates related test utilities to ensure consistency and reliability.

Highlights

  • Temporal Arithmetic Correction: Improved the accuracy of temporal arithmetic operations involving LocalDate, CypherDate, and CypherDuration by correctly carrying over full days from the seconds component of durations.
  • Expression Parsing Order: Adjusted the parsing priority for expressions to ensure top-level list literals are correctly identified before function invocations, resolving potential ambiguities in complex expressions.
  • Query Optimizer Refinement: Enhanced the Cypher query optimizer to intelligently disable itself for MATCH clauses containing multiple disconnected path patterns, as these scenarios require Cartesian products not yet supported by the current optimizer.
  • CypherDuration Multiplication Logic: Refined the multiply method for CypherDuration to implement a cascading remainder approach for fractional months and days, ensuring more accurate and Neo4j-compatible duration calculations.
  • Duration Normalization and Nanos Computation: Corrected duration normalization in durationBetween to handle conflicting signs between seconds and calendar components, and improved computeNanos to preserve higher-order nanosecond components during partial updates.
  • Test Infrastructure Updates: Updated test cases in CypherFunctionFactoryExtendedTest to align with API changes (using getInstance() for DefaultSQLFunctionFactory) and improved type safety with explicit type hints.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • engine/src/main/java/com/arcadedb/query/opencypher/ast/ArithmeticExpression.java
    • Implemented logic to carry over full days from the seconds component of a CypherDuration when performing arithmetic with LocalDate and CypherDate.
  • engine/src/main/java/com/arcadedb/query/opencypher/parser/ExpressionTypeDetector.java
    • Reordered parsing logic in tryParsePrimary to prioritize top-level list literals over function invocations to prevent incorrect parsing of expressions like [date({...})].
  • engine/src/main/java/com/arcadedb/query/opencypher/planner/CypherExecutionPlanner.java
    • Added a condition to shouldUseOptimizer to disable the optimizer for MATCH clauses containing multiple disconnected path patterns, as these require Cartesian products not yet supported.
  • engine/src/main/java/com/arcadedb/query/opencypher/temporal/CypherDuration.java
    • Updated the multiply method to use a cascading remainder approach for fractional months and days, aligning duration multiplication with Neo4j's temporal behavior.
  • engine/src/main/java/com/arcadedb/query/opencypher/temporal/TemporalUtil.java
    • Improved durationBetween to normalize durations by ensuring consistent sign handling between seconds and calendar components (days/months).
    • Modified computeNanos to preserve unspecified higher-order nanosecond portions (e.g., milliseconds, microseconds) when only lower-order parts are provided in a map.
  • engine/src/test/java/com/arcadedb/query/opencypher/executor/CypherFunctionFactoryExtendedTest.java
    • Updated test setup and assertions to use DefaultSQLFunctionFactory.getInstance() instead of the deprecated INSTANCE field.
    • Added a type hint <Object> to getProperty("result") for improved type safety.
Activity
  • The author, lvca, created this pull request to complete 100% temporal OpenCypher TCK.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@lvca lvca added this to the 26.2.1 milestone Feb 8, 2026
@lvca lvca merged commit a4f11f1 into main Feb 8, 2026
7 of 10 checks passed
@lvca lvca deleted the opencypher-tck branch February 8, 2026 04:18
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces several changes to complete the temporal OpenCypher TCK, enhancing temporal arithmetic, duration multiplication, and parsing logic. The changes appear to correctly address issues required for TCK compliance. My review focuses on improving code clarity, maintainability, and robustness. I've suggested replacing magic numbers with named constants, refactoring duplicated code into a helper method, and strengthening a fragile heuristic in the expression parser. Additionally, I've proposed a simplification for a complex method to improve its readability.

if (listCtx != null) {
final String listText = listCtx.getText();
final String exprText = ctx.getText();
if (listText.length() >= exprText.length() - 2) // Top-level list (allow for whitespace)
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The heuristic listText.length() >= exprText.length() - 2 to detect a top-level list is fragile as it may fail with varying amounts of whitespace. A more robust approach would be to trim the expression's text and perform a string comparison.

Suggested change
if (listText.length() >= exprText.length() - 2) // Top-level list (allow for whitespace)
if (listText.equals(exprText.trim())) // Top-level list

// java.time.LocalDate (from ArcadeDB storage) ± Duration
if (leftValue instanceof LocalDate ld && rightValue instanceof CypherDuration dur) {
// For dates, full days from the seconds component must be carried over
final long extraDays = dur.getSeconds() / 86400;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The magic number 86400 for seconds in a day makes the code harder to understand. Consider using java.time.Duration for this conversion to improve readability and type safety.

Suggested change
final long extraDays = dur.getSeconds() / 86400;
final long extraDays = java.time.Duration.ofSeconds(dur.getSeconds()).toDays();

Comment on lines +217 to +221
// For dates, full days from the seconds component must be carried over
final long extraDays = dur.getSeconds() / 86400;
final long totalDays = dur.getDays() + extraDays;
final LocalDate d = cd.getValue().plusMonths(op == Operator.ADD ? dur.getMonths() : -dur.getMonths())
.plusDays(op == Operator.ADD ? dur.getDays() : -dur.getDays());
.plusDays(op == Operator.ADD ? totalDays : -totalDays);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This block of code is nearly identical to lines 197-201, which handles LocalDate. To avoid code duplication and improve maintainability, consider extracting this logic for applying a CypherDuration to a LocalDate into a private helper method.

final long wholeMonths = (long) newMonths;
final double monthRemainder = newMonths - wholeMonths;

final double newDays = days * factor + monthRemainder * (365.2425 / 12);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The magic number 365.2425 / 12 represents the average number of days in a month. It would be clearer to define this as a named constant, e.g., private static final double AVG_DAYS_PER_MONTH = 365.2425 / 12.0;. This value is also used in the fromComponents method, so a constant would improve consistency.

final long wholeDays = (long) newDays;
final double dayRemainder = newDays - wholeDays;

final double totalNanos = (seconds * 1_000_000_000.0 + nanosAdjustment) * factor + dayRemainder * 86400_000_000_000.0;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The magic numbers for nanoseconds (1_000_000_000.0 and 86400_000_000_000.0) make this line difficult to read. Consider defining constants like NANOS_PER_SECOND and NANOS_PER_DAY to improve clarity and maintainability.

Comment on lines 362 to 375
int nanos = 0;
if (hasMs)
nanos += ((Number) map.get("millisecond")).intValue() * 1_000_000;
else
nanos += (defaultNanos / 1_000_000) * 1_000_000;
if (hasUs)
nanos += ((Number) map.get("microsecond")).intValue() * 1_000;
else
nanos += ((defaultNanos % 1_000_000) / 1_000) * 1_000;
if (hasNs)
nanos += ((Number) map.get("nanosecond")).intValue();
else
nanos += defaultNanos % 1_000;
return nanos;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The logic for computing nanoseconds is verbose and repetitive. It can be simplified for better readability by calculating each component (milli, micro, nano) based on whether it's present in the map or should be taken from the default, and then combining them at the end.

For example:

final int NANOS_PER_MILLI = 1_000_000;
final int NANOS_PER_MICRO = 1_000;

int milli = hasMs ? ((Number) map.get("millisecond")).intValue() : defaultNanos / NANOS_PER_MILLI;
int micro = hasUs ? ((Number) map.get("microsecond")).intValue() : (defaultNanos % NANOS_PER_MILLI) / NANOS_PER_MICRO;
int nano  = hasNs ? ((Number) map.get("nanosecond")).intValue()  : defaultNanos % NANOS_PER_MICRO;

return milli * NANOS_PER_MILLI + micro * NANOS_PER_MICRO + nano;

@mergify
Copy link
Contributor

mergify bot commented Feb 8, 2026

🧪 CI Insights

Here's what we observed from your CI run for 9d5bf37.

🟢 All jobs passed!

But CI Insights is watching 👀

@claude
Copy link

claude bot commented Feb 8, 2026

Code Review: Completed 100% temporal OpenCypher TCK

Great work achieving 100% compliance with the OpenCypher TCK temporal tests!

Overall Assessment: Approve with minor suggestions

The changes successfully fix temporal handling in OpenCypher to achieve full TCK compliance. Code quality is generally good with clear comments explaining complex logic.


Detailed Findings:

  1. ArithmeticExpression.java (Lines 197-202, 217-222)
    GOOD: Correctly converts duration seconds to days when added to dates
    CONCERN: Consider test cases for negative durations with mixed signs

  2. ExpressionTypeDetector.java (Lines 190-209)
    GOOD: Fixes precedence issue where [date({...})] was parsed as function call
    CONCERN: Magic number -2 in length comparison is fragile; duplicate parsing of listCtx

  3. CypherExecutionPlanner.java (Lines 143-146)
    GOOD: Correctly disables optimizer for disconnected patterns requiring Cartesian products

  4. CypherDuration.multiply() (Lines 155-172)
    EXCELLENT: Sophisticated fractional duration multiplication matching Neo4j/OpenCypher spec
    CONCERN: No overflow checking for totalNanos with extreme values; floating-point precision loss possible

  5. TemporalUtil.durationBetween() (Lines 321-343)
    GOOD: Comprehensive sign normalization
    CRITICAL BUG: Line 339-342 only carries seconds to days when months > 0 OR days > 0
    If both are 0 but seconds >= 86400, normalization fails!
    FIX: Remove the calendar check condition at line 339

  6. TemporalUtil.computeNanos() (Lines 362-375)
    EXCELLENT: Preserves higher-order portions from defaultNanos correctly
    CONCERN: Add validation for out-of-range values (e.g., millisecond: 9999)


Testing: No new test files in PR (only TCK compliance achieved)
RECOMMEND: Add edge case tests for extreme values, mixed signs, overflow scenarios

Security: No issues identified
Performance: Minimal impact

Code Style: Follows project standards well
Minor: Some magic numbers could be named constants (29, 365.2425/12, 86400_000_000_000.0)


Action Items BEFORE MERGING:

  1. FIX CRITICAL BUG in TemporalUtil line 339 - remove calendar check condition
  2. Add validation for nanosecond component ranges
  3. Clarify the 29 days magic number in duration normalization
  4. Consider adding regression tests

Future improvements: overflow protection, extract magic constants


Summary: Excellent work! Main concern is the potential bug in TemporalUtil line 339.
Recommendation: Approve after addressing the critical fix.

Congratulations on 100% TCK temporal compliance!

@codacy-production
Copy link

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
-11.15% 24.24%
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (2374b6b) 80848 56010 69.28%
Head commit (9d5bf37) 110438 (+29590) 64193 (+8183) 58.13% (-11.15%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#3369) 33 8 24.24%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

@codecov
Copy link

codecov bot commented Feb 8, 2026

Codecov Report

❌ Patch coverage is 21.21212% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.82%. Comparing base (2374b6b) to head (9d5bf37).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...dedb/query/opencypher/temporal/CypherDuration.java 0.00% 11 Missing ⚠️
...cadedb/query/opencypher/temporal/TemporalUtil.java 0.00% 8 Missing ⚠️
...edb/query/opencypher/ast/ArithmeticExpression.java 0.00% 6 Missing ⚠️
...ery/opencypher/planner/CypherExecutionPlanner.java 50.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3369      +/-   ##
==========================================
+ Coverage   60.59%   60.82%   +0.23%     
==========================================
  Files        1177     1177              
  Lines       80848    80874      +26     
  Branches    16231    16235       +4     
==========================================
+ Hits        48988    49193     +205     
+ Misses      24865    24713     -152     
+ Partials     6995     6968      -27     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

robfrank pushed a commit that referenced this pull request Feb 17, 2026
* test: including official opencypher tck

* fix: fixed many issues with opencypher from TCK tests

* fix: fixed broken tests from OpenCyphr TCK

* fix: more opencypher issues from tck tests

* fix: opencypher tck more tests pass now

Reached much better compliance with the latest changes:

```
=======================================================
         OpenCypher TCK Compliance Report
=======================================================
Total scenarios:  3897
Passed:           1908 (48%)
Failed:           1939 (49%)
Skipped:          50 (1%)
-------------------------------------------------------
By category:
  clauses/call                               2/ 52 passed (3%)
  clauses/create                            64/ 78 passed (82%)
  clauses/delete                            24/ 41 passed (58%)
  clauses/match                            292/381 passed (76%)
  clauses/match-where                       25/ 34 passed (73%)
  clauses/merge                             47/ 75 passed (62%)
  clauses/remove                            29/ 33 passed (87%)
  clauses/return                            35/ 63 passed (55%)
  clauses/return-orderby                    23/ 35 passed (65%)
  clauses/return-skip-limit                 26/ 31 passed (83%)
  clauses/set                               30/ 53 passed (56%)
  clauses/union                              8/ 12 passed (66%)
  clauses/unwind                            10/ 14 passed (71%)
  clauses/with                              14/ 29 passed (48%)
  clauses/with-orderBy                     124/292 passed (42%)
  clauses/with-skip-limit                    7/  9 passed (77%)
  clauses/with-where                        10/ 19 passed (52%)
  expressions/aggregation                   23/ 35 passed (65%)
  expressions/boolean                      150/150 passed (100%)
  expressions/comparison                    36/ 72 passed (50%)
  expressions/conditional                   13/ 13 passed (100%)
  expressions/existentialSubqueries          4/ 10 passed (40%)
  expressions/graph                         32/ 61 passed (52%)
  expressions/list                         120/185 passed (64%)
  expressions/literals                     120/131 passed (91%)
  expressions/map                           28/ 44 passed (63%)
  expressions/mathematical                   3/  6 passed (50%)
  expressions/null                          44/ 44 passed (100%)
  expressions/path                           0/  7 passed (0%)
  expressions/pattern                       19/ 50 passed (38%)
  expressions/precedence                    20/121 passed (16%)
  expressions/quantifier                   478/604 passed (79%)
  expressions/string                        22/ 32 passed (68%)
  expressions/temporal                       0/1004 passed (0%)
  expressions/typeConversion                19/ 47 passed (40%)
  useCases/countingSubgraphMatches           6/ 11 passed (54%)
  useCases/triadicSelection                  1/ 19 passed (5%)
=======================================================
```

* fix: opencypher implemented missing temporal functions + precedence

Issue #3357 and #3355

* fix: opencypher implemented more missing functions

Issue #3357 and #3355

* fix: opencypher more code fixed thank to the tck

* fix: OpenCypher fixed temporal functions (from TCK results)

* fix: OpenCypher completed 100% of temporal tests (!)

Issue #3357

(cherry picked from commit a4f11f1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant