@@ -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,9 @@ 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
159169 return n
160170}
161171
@@ -201,14 +211,22 @@ func (s *SpanSet) AddMVCC(access SpanAccess, span roachpb.Span, timestamp hlc.Ti
201211 s.spans [access ][scope ] = append (s.spans [access ][scope ], Span {Span : span , Timestamp : timestamp })
202212}
203213
214+ // AddForbiddenMatcher adds a forbidden span matcher. The matcher is a function
215+ // that is called for each span access to check if it should be forbidden.
216+ func (s * SpanSet ) AddForbiddenMatcher (matcher func (roachpb.Span ) error ) {
217+ s .forbiddenSpansMatchers = append (s .forbiddenSpansMatchers , matcher )
218+ }
219+
204220// Merge merges all spans in s2 into s. s2 is not modified.
205221func (s * SpanSet ) Merge (s2 * SpanSet ) {
206222 for sa := SpanAccess (0 ); sa < NumSpanAccess ; sa ++ {
207223 for ss := SpanScope (0 ); ss < NumSpanScope ; ss ++ {
208224 s.spans [sa ][ss ] = append (s.spans [sa ][ss ], s2.spans [sa ][ss ]... )
209225 }
210226 }
227+ s .forbiddenSpansMatchers = append (s .forbiddenSpansMatchers , s2 .forbiddenSpansMatchers ... )
211228 s .allowUndeclared = s2 .allowUndeclared
229+ s .allowForbidden = s2 .allowForbidden
212230 s .SortAndDedup ()
213231}
214232
@@ -331,9 +349,19 @@ func (s *SpanSet) CheckAllowedAt(
331349func (s * SpanSet ) checkAllowed (
332350 access SpanAccess , span roachpb.Span , check func (SpanAccess , Span ) bool ,
333351) error {
352+ // Unless explicitly disabled, check if we access any forbidden spans.
353+ if ! s .allowForbidden {
354+ // Check if the span is forbidden.
355+ for _ , matcher := range s .forbiddenSpansMatchers {
356+ if err := matcher (span ); err != nil {
357+ return errors .Errorf ("cannot %s span %s: matches forbidden pattern" ,
358+ access , span )
359+ }
360+ }
361+ }
362+
363+ // Unless explicitly disabled, check if we access any undeclared spans.
334364 if s .allowUndeclared {
335- // If the request has specified that undeclared spans are allowed, do
336- // nothing.
337365 return nil
338366 }
339367
@@ -352,6 +380,8 @@ func (s *SpanSet) checkAllowed(
352380 }
353381
354382 return errors .Errorf ("cannot %s undeclared span %s\n declared:\n %s\n stack:\n %s" , access , span , s , debugutil .Stack ())
383+
384+ return nil
355385}
356386
357387// contains returns whether s1 contains s2. Unlike Span.Contains, this function
@@ -396,3 +426,8 @@ func (s *SpanSet) Validate() error {
396426func (s * SpanSet ) DisableUndeclaredAccessAssertions () {
397427 s .allowUndeclared = true
398428}
429+
430+ // DisableForbiddenAssertions disables forbidden spans assertions.
431+ func (s * SpanSet ) DisableForbiddenSpansAssertions () {
432+ s .allowForbidden = true
433+ }
0 commit comments