You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 9dd4a74
Browse filesBrowse the repository at this point in the historyBrowse files
fix(go): a plan field's context is a label, refused at construction
Codex, on PR #1093: the lowering refuses every plan field whose context is
not a label of at least two plain segments, but NewPlan and PlanFromTags
still accepted a one-part Context (MustContext("c"), the context= tag), a
one-segment label, bytes or an integer, and Context.With on a field, so
such a plan built fine and every record call then failed with ErrEncoding.
The mistake belongs at the earliest stage (Go principle 1), so the Go side
now refuses at plan construction what the lowering refuses.
Context.fieldLabel is the rule, one place: a planned field's Context must
be a flat list of plain text parts, two or more, and is read as that Label.
A one-part context, an extended context and a part that is not text are
refused with the field named and the accepted form in the message; a flat
list built with NewContext("users").With("age") is the label it spells, as
the lowering reads it. NewPlan applies it to every field; PlanFromTags
refuses the context= tag outright, naming label= as the tag to use, after
the option loop so a repeated or doubled option is still reported as what
the author wrote.
plan.Custom took one arbitrary text part and bound it with NewContext; it
now parses its argument as a Label, so a Custom target binds a label of
two or more plain segments and a one-segment or unplain one is refused at
Build. plantest reads a Custom column's context the same way. The golden
files do not change: a Custom context was always rendered as the text it
was written as, which is a label's String() too.
Two of the lowering's rules stay with the record call, documented on
NewPlan: every field of a plan must sit under one table, and no two fields
may bind one label. NewPlan cannot hold them without refusing policies the
plan package and its golden tests pin (a Custom target beside a table's
EQL columns; several fields under one Custom context), and that package is
replaced by the next PR in the stack.
Docs follow: Context, NewContext, MustContext, FieldPlan.Context, NewPlan,
the stash tag table, Label's naming table and plan.Custom now say which
contexts a planned field binds and which a probe takes. Tests cover each
refusal; the tests that used the one-part form are rewritten to labels.
Claude-Session: https://claude.ai/code/session_01V3WFXwax4J3uecpFEJ6yHc
// MustContext is [NewContext] for a part known to be valid; it panics
59
-
// otherwise, an empty part included. For string literals in plans and
60
-
// probes.
72
+
// otherwise, an empty part included. For string literals in probes.
61
73
funcMustContext(partany) Context {
62
74
c, err:=NewContext(part)
63
75
iferr!=nil {
@@ -91,6 +103,35 @@ func ownPart(part any) any {
91
103
// lists of scalars, ready for the transport codec.
92
104
func (cContext) value() any { returnc.node }
93
105
106
+
// fieldLabel is the context a planned field may bind, as the guest's
107
+
// lowering reads it: a label of at least two plain segments and nothing
108
+
// else, returned as that Label. A one-part context, an extended context and
109
+
// a part that is not text are refused with a reason that says what is
110
+
// accepted; a flat list of plain text parts is the label it spells,
111
+
// whichever constructor built it.
112
+
func (cContext) fieldLabel() (Label, error) {
113
+
parts, ok:=c.node.([]any)
114
+
if!ok {
115
+
returnLabel{}, errors.New(`is one part, not a label; a planned field binds a label of at least two plain segments, ParseLabel("table/column").Context()`)
116
+
}
117
+
segments:=make([]string, 0, len(parts))
118
+
for_, part:=rangeparts {
119
+
s, ok:=part.(string)
120
+
if!ok {
121
+
returnLabel{}, errors.New("is extended, or holds a part that is not text; a planned field binds a plain label, and a record call extends every field's label alike with ExtendContext")
122
+
}
123
+
segments=append(segments, s)
124
+
}
125
+
iflen(segments) <2 {
126
+
returnLabel{}, errors.New("has one segment; a planned field binds a label of at least two")
127
+
}
128
+
l, err:=NewLabel(segments...)
129
+
iferr!=nil {
130
+
returnLabel{}, fmt.Errorf("is not a plain label: %w", err)
131
+
}
132
+
returnl, nil
133
+
}
134
+
94
135
// checkRootNonEmpty refuses the bare parts that are themselves an empty
95
136
// context. Integers never are, whatever their value.
return"", fmt.Errorf("plantest: column %q: cannot spell the context of target %v; a target that does not bind its column identity must be plan.Custom", fp.Name, target)
0 commit comments