@@ -83,8 +83,14 @@ type Span struct {
8383// The Span slice for a particular access and scope contains non-overlapping
8484// spans in increasing key order after calls to SortAndDedup.
8585type SpanSet struct {
86- spans [NumSpanAccess ][NumSpanScope ][]Span
87- allowUndeclared bool
86+ spans [NumSpanAccess ][NumSpanScope ][]Span
87+ // forbiddenSpansMatchers are functions that return an error if the given span
88+ // shouldn't be accessed (forbidden). This allows for complex pattern matching
89+ // like forbidding specific keys across all range IDs without enumerating them
90+ // explicitly.
91+ forbiddenSpansMatchers []func (roachpb.Span ) error
92+ allowUndeclared bool
93+ allowForbidden bool
8894}
8995
9096var spanSetPool = sync.Pool {
@@ -113,6 +119,8 @@ func (s *SpanSet) Release() {
113119 s.spans [sa ][ss ] = recycle
114120 }
115121 }
122+ s .forbiddenSpansMatchers = nil
123+ s .allowForbidden = false
116124 s .allowUndeclared = false
117125 spanSetPool .Put (s )
118126}
@@ -155,7 +163,19 @@ func (s *SpanSet) Copy() *SpanSet {
155163 n.spans [sa ][ss ] = append (n.spans [sa ][ss ], s.spans [sa ][ss ]... )
156164 }
157165 }
166+ n .forbiddenSpansMatchers = append (n .forbiddenSpansMatchers , s .forbiddenSpansMatchers ... )
158167 n .allowUndeclared = s .allowUndeclared
168+ n .allowForbidden = s .allowForbidden
169+ return n
170+ }
171+
172+ // ShallowCopy performs a shallow SpanSet copy.
173+ func (s * SpanSet ) ShallowCopy () * SpanSet {
174+ n := New ()
175+ n .spans = s .spans
176+ n .forbiddenSpansMatchers = s .forbiddenSpansMatchers
177+ n .allowUndeclared = s .allowUndeclared
178+ n .allowForbidden = s .allowForbidden
159179 return n
160180}
161181
@@ -201,14 +221,22 @@ func (s *SpanSet) AddMVCC(access SpanAccess, span roachpb.Span, timestamp hlc.Ti
201221 s.spans [access ][scope ] = append (s.spans [access ][scope ], Span {Span : span , Timestamp : timestamp })
202222}
203223
224+ // AddForbiddenMatcher adds a forbidden span matcher. The matcher is a function
225+ // that is called for each span access to check if it should be forbidden.
226+ func (s * SpanSet ) AddForbiddenMatcher (matcher func (roachpb.Span ) error ) {
227+ s .forbiddenSpansMatchers = append (s .forbiddenSpansMatchers , matcher )
228+ }
229+
204230// Merge merges all spans in s2 into s. s2 is not modified.
205231func (s * SpanSet ) Merge (s2 * SpanSet ) {
206232 for sa := SpanAccess (0 ); sa < NumSpanAccess ; sa ++ {
207233 for ss := SpanScope (0 ); ss < NumSpanScope ; ss ++ {
208234 s.spans [sa ][ss ] = append (s.spans [sa ][ss ], s2.spans [sa ][ss ]... )
209235 }
210236 }
237+ s .forbiddenSpansMatchers = append (s .forbiddenSpansMatchers , s2 .forbiddenSpansMatchers ... )
211238 s .allowUndeclared = s2 .allowUndeclared
239+ s .allowForbidden = s2 .allowForbidden
212240 s .SortAndDedup ()
213241}
214242
@@ -331,9 +359,19 @@ func (s *SpanSet) CheckAllowedAt(
331359func (s * SpanSet ) checkAllowed (
332360 access SpanAccess , span roachpb.Span , check func (SpanAccess , Span ) bool ,
333361) error {
362+ // Unless explicitly disabled, check if we access any forbidden spans.
363+ if ! s .allowForbidden {
364+ // Check if the span is forbidden.
365+ for _ , matcher := range s .forbiddenSpansMatchers {
366+ if err := matcher (span ); err != nil {
367+ return errors .Errorf ("cannot %s span %s: matches forbidden pattern" ,
368+ access , span )
369+ }
370+ }
371+ }
372+
373+ // Unless explicitly disabled, check if we access any undeclared spans.
334374 if s .allowUndeclared {
335- // If the request has specified that undeclared spans are allowed, do
336- // nothing.
337375 return nil
338376 }
339377
@@ -396,3 +434,8 @@ func (s *SpanSet) Validate() error {
396434func (s * SpanSet ) DisableUndeclaredAccessAssertions () {
397435 s .allowUndeclared = true
398436}
437+
438+ // DisableForbiddenAssertions disables forbidden spans assertions.
439+ func (s * SpanSet ) DisableForbiddenSpansAssertions () {
440+ s .allowForbidden = true
441+ }
0 commit comments