Skip to content

fix(processor): null the current row when SET returns a NULL value (fixes #1136) - #1146

Merged
adsharma merged 3 commits into
LadybugDB:mainfrom
Tyagiquamar:fix-set-null-return-row
Oct 9, 2026
Merged

adsharma merged 3 commits into
LadybugDB:mainfrom
Tyagiquamar:fix-set-null-return-row

Conversation

@Tyagiquamar

Copy link
Copy Markdown
Contributor

Fixes #1136.

Problem. When SET x.prop = NULL ... RETURN x.prop updates more than one row, only the first row of the chunk comes back as NULL. Every other row returns its old value, even though the stored value is NULL:

-- Person rows (id, age): (1, 21), (2, 22), (3, 23), (4, 24)
MATCH (p:Person) SET p.age = NULL RETURN p.id, p.age;
-- returns 1|, 2|22, 3|23, 4|24 instead of 1|, 2|, 3|, 4|

This affects node and rel properties, single and multi-label SET. Setting a non-NULL value is not affected.

Root cause. writeColumnUpdateResult copies the new value into the projected column (columnVector) at the column's current position, columnSelVector[0]. The NULL branch instead used dataSelVector[0], the position in the evaluated value's vector. For a literal that vector has its own state, so the position is always 0. On every row of a flattened chunk, it nulled row 0 of the projected column and left the current row alone.

The four places that null the projected column when the property does not exist in the table, or when the table is not in the label set, used the same wrong position (columnDataVector's selection vector). Today the projected value for those rows is already NULL from the scan, so I could not produce a visible wrong result through them. They still wrote to the wrong row, though, so they now use the column's own position too.

Fix. Use columnVector's selection position in all five setNull calls, matching the non-NULL branch.

Tests.

  • dml_node/set/set_read.test, new case SetNullReadMultipleRows: multi-row SET ... = NULL RETURN for INT64 and STRING properties, a follow-up read, and the multi-label path (MATCH (a:organisation:play)).
  • dml_rel/set/set_read_tinysnb.test, new case SetNullReadMultipleRows: the same for a rel property over three rels.

clang-format-18 reports no changes for set_executor.cpp. I have not run the e2e suite locally (my local build did not finish), so CI is the first run of these tests.

…ixes LadybugDB#1136)

writeColumnUpdateResult marked the projected column NULL at the position taken from the evaluated value's selection vector. For a literal NULL that is always position 0, so only the first row of the chunk was nulled and every later row returned its old value. Use the projected column's own position, as the non-NULL branch already does. The paths that null the column for a missing property or an unmatched table had the same mistake.
@adsharma

adsharma commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

The code looks good. Could you update the tests?

  • Low: 4 of 5 sites (set_executor.cpp:49,71,107,127) have no reproducing query — logically right but unverified; mixed-label case would confirm.
  • Low: test reuses table play (set_read.test:96-104), only covers literal-NULL not expr-evaluating-to-NULL.

@Tyagiquamar

Copy link
Copy Markdown
Contributor Author

Updated the test coverage in est/test_files/dml_node/set/set_read.test and est/test_files/dml_rel/set/set_read_tinysnb.test in commit 9609da1:

  1. Expressions evaluating to NULL: added test cases using
    ullif(...) for both node properties (�ge, Name) and rel properties (date).
  2. Multi-label node and rel verification: replaced table play creation with existing inysnb entities (person:organisation for node SET, and knows|studyAt for rel SET), confirming the projected null column behavior across multi-label matches where one entity possesses the property and another does not (exercising executor paths in both directions for �ge/score and date/year).

@Tyagiquamar

Copy link
Copy Markdown
Contributor Author

Could you update the tests?

The mixed-label and expression-to-NULL cases were added in 9609da1, but CI found that a relationship test assigned an INT64 expression to a DATE property. The follow-up uses DATE-valued nullif expressions and restores non-NULL node and relationship properties before the expression cases, so the assertions cover a real value-to-NULL transition.

Validation in Ubuntu 24.04 Docker with GCC 13.3.0:

  • cmake --build build/release --target e2e_test -j 4: passed.
  • E2E_TEST_FILES_DIRECTORY=test/test_files build/release/test/runner/e2e_test --gtest_filter="*set_read*": all 9 tests passed, including both SetNullReadMultipleRows cases.
  • clang-format-18 --dry-run --Werror src/processor/operator/persistent/set_executor.cpp: passed.
  • git diff --check: passed.

The full suite is left to PR CI.

@adsharma
adsharma merged commit 5943adc into LadybugDB:main Oct 9, 2026
4 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.

Bug: SET p.prop = NULL ... RETURN p.prop returns the old value for every row except the first

2 participants