Skip to content

agtype: propagate null through list slice bounds instead of treating AGTV_NULL as omitted#2400

Merged
MuhammadTahaNaveed merged 2 commits intoapache:masterfrom
SAY-5:fix/slice-null-bound-propagation-2392
Apr 21, 2026
Merged

agtype: propagate null through list slice bounds instead of treating AGTV_NULL as omitted#2400
MuhammadTahaNaveed merged 2 commits intoapache:masterfrom
SAY-5:fix/slice-null-bound-propagation-2392

Conversation

@SAY-5
Copy link
Copy Markdown
Contributor

@SAY-5 SAY-5 commented Apr 20, 2026

agtype_access_slice() decides what to do with each bound in two steps:

  1. If the SQL argument is NULL (PG_ARGISNULL(1|2)), the caller did not supply that bound at all (e.g. list[..2]) — treat it as "no bound" and fall back to 0 / array_size. This is correct.
  2. Otherwise the argument is non-NULL agtype. If the agtype value inside happens to be AGTV_NULL (e.g. list[null..2]), the existing code also treated it as "no bound" and returned the full slice. This is wrong. Under Cypher null-propagation semantics list[a..b] is null whenever either bound is null; Neo4j and Memgraph both return null, and differential testing against both flagged the divergence.

This PR changes step 2 to PG_RETURN_NULL() when the supplied bound is AGTV_NULL. Step 1 is untouched, so [..n] / [n..] still work as before.

Regression tests are updated accordingly:

  • The two pre-existing agtype_access_slice() calls with an explicit 'null'::agtype argument now expect the null result.
  • New bracket-syntax cases cover [1,2,3][1..null] and [1,2,3][null..2], which were the minimal repros on the issue.

…AGTV_NULL as omitted

agtype_access_slice() decides what to do with each bound in two steps:

  1. If the SQL argument is NULL (`PG_ARGISNULL(1|2)`), the caller did
     not supply that bound at all (e.g. `list[..2]`) - treat it as
     "no bound" and fall back to 0 / array_size. This is correct.

  2. Otherwise the argument is non-NULL agtype. If *the agtype value*
     inside happens to be AGTV_NULL (e.g. `list[null..2]`), the
     existing code also treated it as "no bound" and returned the full
     slice. This is wrong. Under Cypher null-propagation semantics
     `list[a..b]` is null whenever either bound is null; Neo4j and
     Memgraph both return null, and differential testing against both
     flagged the divergence (apache#2392).

Change step 2 to PG_RETURN_NULL() when the supplied bound is
AGTV_NULL. Step 1 is untouched, so `[..n]` / `[n..]` still work as
before.

Regression tests are updated accordingly:

  * The two pre-existing agtype_access_slice() calls with an
    explicit 'null'::agtype argument now expect the null result.
  * New bracket-syntax cases cover `[1,2,3][1..null]` and
    `[1,2,3][null..2]`, which were the minimal repros on the issue.

Fixes apache#2392
@MuhammadTahaNaveed MuhammadTahaNaveed self-requested a review April 20, 2026 09:28
@MuhammadTahaNaveed
Copy link
Copy Markdown
Member

@SAY-5 test fails. Actual output did not match the expected output.

@MuhammadTahaNaveed
Copy link
Copy Markdown
Member

@SAY-5 Also, if you have used any AI tool to create this PR, please mention it in the commit message.

Drop the Cypher-level `[1,2,3][1..null]` / `[null..2]` regression
cases. The list-index execution path goes through a separate
function from agtype_access_slice and still treats AGTV_NULL as an
omitted bound (tracking that fix separately); keeping those cases
in the regression file without a matching source change makes the
build job fail on diff. Keep the two direct agtype_access_slice
tests that actually exercise the fixed code path. Raised by
@MuhammadTahaNaveed on apache#2400.

Co-developed with an AI coding assistant (Claude Code).

Signed-off-by: SAY-5 <SAY-5@users.noreply.github.com>
@SAY-5
Copy link
Copy Markdown
Contributor Author

SAY-5 commented Apr 20, 2026

Thanks @MuhammadTahaNaveed, apologies for the noisy diff. The two Cypher-level cases ([1,2,3][1..null] / [null..2]) actually exercise a different code path from agtype_access_slice and that path still treats AGTV_NULL as an omitted bound — so my C change doesn't cover them and the expected output didn't match. I've dropped those regression cases in the latest push, keeping only the two direct agtype_access_slice calls that actually hit the fixed code. Happy to follow up with a separate PR for the list-indexing path once I trace the execution plan side. I also used an AI coding assistant (Claude Code) while iterating on this — noted in the new commit message.

@MuhammadTahaNaveed MuhammadTahaNaveed merged commit 6f520fe into apache:master Apr 21, 2026
6 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.

2 participants