fix(cypher): reject a non-numeric SKIP/LIMIT operand instead of dropping it - #2002
Conversation
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
|
Approved on merit. Both CI failures are unrelated to your change — details below — so this needs a rebase and a re-run, not a code change. The diagnosis holdsI checked it against
The fix is the right shape because it removes an inconsistency rather than adding a rule. The The measured before-table is what makes this reviewable in one pass — The two red checks
I did look for a mechanism by which stricter Please rebase — you are 3 commits behind ( Your
|
…ing it
expect() fills p->error and returns NULL when the token after SKIP or LIMIT
is not a number, but both call sites only tested `if (num)` and then fell
through to `*out = r; return 0;`. r->limit kept the -1 "unset" initialiser,
which execution reads as "no LIMIT":
rb_apply_skip_limit(rb, ret->skip, ret->limit >= 0 ? ret->limit : max_rows);
So `RETURN n.name LIMIT $limit` ran unbounded and answered isError:false with
the whole result set. `SKIP $offset LIMIT 10` lost both clauses at once,
because the orphaned operand also blocked the following match(p, TOK_LIMIT).
Propagate the failure the way the ORDER BY branch directly above already
does. This is the failure mode DeusData#1334 banned, reached by a different route.
Fixes DeusData#1994
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: metehanulusoy <ulusoy.metehan03@gmail.com>
976f312 to
3d9a6cf
Compare
|
Rebased onto On the Thanks for tracing the two red checks yourself rather than bouncing them back to me. |
|
Rebased and re-running — thank you. You are now 10 commits behind rather than 3 behind a And thank you for filing #2003 for the My review stands: approved on merit, merging on green. For the two reds you saw earlier — both were ours and neither was yours. Fair warning that green may take a while. Our Actions pool is servicing roughly one job at a time against 45 queued runs, so the delay is ours, not yours. |
Fixes #1994
The defect
parse_return_or_with()acceptedSKIP/LIMITonly when the next token was a number. Whenexpect(p, TOK_NUMBER)failed it filledp->errorand returned NULL, but both call sites only testedif (num)and then fell through to*out = r; return 0;.r->limitkept the-1"unset" initialiser, which execution reads as "no LIMIT":So a bounded query ran unbounded and reported success.
SKIP $offset LIMIT 10lost both clauses at once, because the orphaned operand also blocked the followingmatch(p, TOK_LIMIT).Measured on the built binary before the fix, against a 26-node fixture:
MATCH (n) RETURN n.name LIMIT 1MATCH (n) RETURN n.name LIMIT $limitMATCH (n) RETURN n.name SKIP $offset LIMIT 1MATCH (n) RETURN n.name LIMIT abcThe change
Propagate the
expect()failure in both branches —free_return_clause(r); return CBM_NOT_FOUND;— exactly as theORDER BYbranch directly above already does.cbm_parse()then surfaces the existingexpected token type ... got ... at pos Nmessage instead of silently continuing.This is the failure mode #1334 banned ("the old failure mode — ignore the remainder, drop the LIMIT — must never come back"), reached by a different route.
Tests
Four cases in
tests/test_cypher.c, next to the #1334 regression:cypher_parse_nonnumeric_limit_rejected_issue1994—LIMIT $limitcypher_parse_nonnumeric_skip_rejected_issue1994—SKIP $offset LIMIT 10, covering the swallowed second clausecypher_parse_word_limit_operand_rejected_issue1994—LIMIT abccypher_parse_numeric_skip_limit_still_accepted— control:SKIP 2 LIMIT 10still parses and still carries 2/10The first three fail on
mainand pass with this change; the control passes both ways.Verification
scripts/test.sh— green (clean ASan+UBSan build, all suites, contract steps, prod-binary guards)make lint-formatwithclang-format-20(the version_lint.ymlpins) — no driftmake lint-tidy-diff— cleancppcheck— cleanmain(1778637) — not staleOne note on how I ran that:
scripts/test.shis green when invoked directly, but thescripts/hookspre-commit hook failed five times in a row ongit_context_linked_worktree. That turned out to be unrelated to this change — git exportsGIT_DIRinto hook environments,GIT_DIRoverridesgit -C, and the git-shelling tests intests/test_pipeline.ctherefore run against the real repository instead of their temp fixture. Reproduced without any hook:GIT_DIR=/path/to/repo/.git scripts/test.sh --suites pipelinefails, plainscripts/test.sh --suites pipelinepasses.mainwith no changes shows the same thing, so it is pre-existing. I filed it separately as #2003 rather than bundling it here; this commit therefore used--no-verify, with the gates run by hand (all listed above).Behavior change — worth calling out
This is stricter than
main: a query whoseSKIP/LIMIToperand is not a number now returns a parse error where it previously returned rows. Anyone who was unknowingly sendingLIMIT $limitwas already getting the wrong answer, so the error is the correct outcome — but it is a visible change, not a pure internal fix, and I would rather flag it than have it surprise you. If you would prefer a warning-and-continue instead of a hard error, say so and I will rework it.Scope
One file changed in
src/, 13 insertions / 6 deletions. No MCP tool signature change, no new dependency, no newsystem()/popen()/network call.