repl: Allow interactive ref head rule definitions - #9001
Merged
sspaink merged 1 commit intoAug 13, 2026
Conversation
| // | ||
| // > a[0] := 1 | ||
| // > a | ||
| // {"0": 1} |
Contributor
There was a problem hiding this comment.
It might be that a user["role"] := "admin" demonstrates a more real use case for this. Just jumped out to me that this is odd to use an example here that looks like an array, but becomes an obj 'because'.
charlieegan3
approved these changes
Aug 13, 2026
Contributor
|
Tested myself and it all seems to work ok. |
Statements such as `a[0] := 1` or `p.q.r := 1` were rejected with "cannot assign to ref". The REPL refused to interpret refs of more than one term as rule heads, so those statements fell through to being compiled as a query body, where assigning to a ref isn't allowed. They are now interpreted as rule definitions. Refs rooted at data or input are excluded, as `data.foo.bar = 1` asks whether that document is 1 rather than defining a rule. Rules are identified by their head ref instead of Head.Name when unsetting them, which fixes two related problems: ref head rules (so far only definable with the `if` keyword) could not be unset at all, because their Head.Name is empty, and re-defining `a[0] := 1` doesn't drop unrelated keys of the same document, e.g. a[1]. As a consequence, `unset` accepts a ref, e.g. `unset a[0]` or `unset p.q.r`, and removes all rules below it. ParsePartialObjectDocRuleFromEqExpr didn't mark the `true` body it generates as generated, so rules parsed from these statements tripped the rego-v1 check requiring `if` before a rule body. The module parser sets that flag itself after calling ParseRuleFromBody, which is why this only surfaced for direct callers like the REPL. Fixes: open-policy-agent#5498 Signed-off-by: Sebastian Spaink <sebastianspaink@gmail.com>
sspaink
force-pushed
the
repl-ref-head-rule-definitions
branch
from
August 13, 2026 17:07
74bf9fd to
578fa9e
Compare
sspaink
enabled auto-merge (squash)
August 13, 2026 17:07
Benchmark Comparison (`8b1b42ee113bfe8bb91609eec951cd3d79c387a3` vs `ee937f696ee0bde8146ade3580687fbd5651331c`)
This comment was automatically generated by the benchmarks workflow. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes: #5498
Statements such as
a[0] := 1orp.q.r := 1were rejected with "cannot assign to ref". The REPL refused to interpret refs of more than one term as rule heads, so those statements fell through to being compiled as a query body, where assigning to a ref isn't allowed.They are now interpreted as rule definitions. Refs rooted at data or input are excluded, as
data.foo.bar = 1asks whether that document is 1 rather than defining a rule.Rules are identified by their head ref instead of Head.Name when unsetting them, which fixes two related problems: ref head rules (so far only definable with the
ifkeyword) could not be unset at all, because their Head.Name is empty, and re-defininga[0] := 1doesn't drop unrelated keys of the same document, e.g. a[1]. As a consequence,unsetaccepts a ref, e.g.unset a[0]orunset p.q.r, and removes all rules below it.ParsePartialObjectDocRuleFromEqExpr didn't mark the
truebody it generates as generated, so rules parsed from these statements tripped the rego-v1 check requiringifbefore a rule body. The module parser sets that flag itself after calling ParseRuleFromBody, which is why this only surfaced for direct callers like the REPL.