Skip to content

Implement expression evaluation in RETURN projections#407

Merged
cyrusknopf merged 56 commits intomainfrom
expr/return
Feb 24, 2026
Merged

Implement expression evaluation in RETURN projections#407
cyrusknopf merged 56 commits intomainfrom
expr/return

Conversation

@cyrusknopf
Copy link
Contributor

@cyrusknopf cyrusknopf commented Feb 9, 2026

Implements #359

Overview:

  • Adds unnamed variable declarations for binary expressions
  • Updates CountProcessor to use new ColumnOperatorDispatcher
  • Implements ExprEvalProcessor to evaluate expressions and store the results in the output dataframe
  • Modifies ExprProgramGenerator with more support for unary expressions
  • Adds support in PipelineGenerator for the new ExprEvalProcessor
  • Adds evaluation logic for functions with expressions-as-arguments


const auto& expectedFirstCol = std::views::iota(1000, 2000);
const auto& expectedSecondCol = std::views::iota(2000, 300);
const auto& expectedSecondCol = std::views::iota(2000, 3000);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixes a bug which was caught by gcc15

Copy link
Contributor

Choose a reason for hiding this comment

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

Do you have the bug logs? I'm curious! If not don't worry about it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can't seem to repro now haha. But it was a static_assert failing because the start of the range (2000) was greater than the end of the range (300)

@cyrusknopf cyrusknopf force-pushed the expr/return branch 2 times, most recently from 8c55eac to 9dacd54 Compare February 17, 2026 12:42
@cyrusknopf cyrusknopf marked this pull request as ready for review February 17, 2026 12:43
@cyrusknopf cyrusknopf requested a review from rjb32 as a code owner February 17, 2026 12:43
@turing-db turing-db deleted a comment from github-actions bot Feb 17, 2026

// Create a variable declaration for the binary expression so that it can be retrieved
// later (for projection or in an expression / filter), e.g. RETURN COUNT(5 + 5)
expr->setExprVarDecl(_ctxt->createUnnamedVariable(_ast, expr->getType()));
Copy link
Contributor

Choose a reason for hiding this comment

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

It would look better if you created a temporary for the variable declaration pointer instead of a nested call line

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

}

void CountProcessor::prepare(ExecutionContext* ctxt) {
void CountProcessor::prepare(ExecutionContext* /*ctxt*/) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Just keep ctxt that's ok

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed


// @ref _col is nullptr in the case of a COUNT(*), which in Cypher means "count the
// number of rows in the output". Unlike a COUNT(n.name), COUNT(*) also includes
// null-valued rows
Copy link
Contributor

Choose a reason for hiding this comment

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

It's a bit weird that there is nothing in that sense in the prepare function which gives the impression that _col will always be non-null

inline constexpr bool is_template_t = is_template<T, Template>::value;

/// Functor to pass to column dispatcher to count rows
struct Counter {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you really need it to be a struct? It is almost always better to have a class. Especially if you actually don't access the public members

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Made Counter a class


Counter counter(localCount);
using Types = OutputtedTypes;
using CountDispatch =
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks very compact otherwise it is much better


// Methods such as @ref Dataframe::getLogicalRowCount return a size_t; ensure types are
// compatible.
static_assert(std::numeric_limits<size_t>::max()
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we rather put this static_assert inside the CountProcessor class?

Copy link
Contributor Author

@cyrusknopf cyrusknopf Feb 17, 2026

Choose a reason for hiding this comment

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

What is the benefit of putting it inside the class?

Copy link
Contributor

@rjb32 rjb32 Feb 17, 2026

Choose a reason for hiding this comment

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

There is none really besides an esthetic one, it avoids having a statement at global scope in principle when it is about a type alias defined with using inside the class

Copy link
Contributor Author

@cyrusknopf cyrusknopf Feb 18, 2026

Choose a reason for hiding this comment

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

Moved the static_assert into the class

const ColumnTag tag = findColIt->second;

items.push_back({tag, *name});

Copy link
Contributor

Choose a reason for hiding this comment

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

Remove blank line here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@cyrusknopf cyrusknopf force-pushed the expr/return branch 2 times, most recently from 8d1b281 to 5ad15b6 Compare February 18, 2026 14:29
@github-actions
Copy link

github-actions bot commented Feb 18, 2026

Claude Code Review

Violations found (click to expand)
File Line Rule Violation
storage/columns/UnaryPredicates.h static void apply(ColumnConst<T>* res, const ColumnConst<T>* arg) requires OptionalPredicate<Op, T> + next line { CODING_STYLE.md — Brackets: "Constructors are the only place where the opening bracket must be on the next line"; methods have { on the same line Opening brace { is on its own line for a static method; it should be on the same line as the requires clause.
storage/columns/UnaryPredicates.h static void apply(ColumnVector<T>* res, const ColumnVector<T>* arg) requires(...) + next line { CODING_STYLE.md — Brackets: "Constructors are the only place where the opening bracket must be on the next line"; methods have { on the same line Opening brace { is on its own line for a static method; it should be on the same line as the requires clause.

Checked against CODING_STYLE.md and REVIEW.md.

@rjb32 false positive, as per our discussion about "requires" clauses after function parameter lists

@cyrusknopf cyrusknopf merged commit d9b5e1c into main Feb 24, 2026
2 checks passed
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.

Unique return item names

3 participants