github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/pkg/regionspan/span.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package regionspan
    15  
    16  import (
    17  	"bytes"
    18  	"encoding/hex"
    19  	"fmt"
    20  
    21  	"github.com/pingcap/log"
    22  	cerror "github.com/pingcap/ticdc/pkg/errors"
    23  	"github.com/pingcap/tidb/kv"
    24  	"github.com/pingcap/tidb/tablecodec"
    25  	"github.com/pingcap/tidb/util/codec"
    26  	"go.uber.org/zap"
    27  )
    28  
    29  // Span represents a arbitrary kv range
    30  type Span struct {
    31  	Start []byte
    32  	End   []byte
    33  }
    34  
    35  // String returns a string that encodes Span in hex format.
    36  func (s Span) String() string {
    37  	return fmt.Sprintf("[%s, %s)", hex.EncodeToString(s.Start), hex.EncodeToString(s.End))
    38  }
    39  
    40  // UpperBoundKey represents the maximum value.
    41  var UpperBoundKey = []byte{255, 255, 255, 255, 255}
    42  
    43  // ComparableSpan represents a arbitrary kv range which is comparable
    44  type ComparableSpan Span
    45  
    46  // String returns a string that encodes ComparableSpan in hex format.
    47  func (s ComparableSpan) String() string {
    48  	return Span(s).String()
    49  }
    50  
    51  // Hack will set End as UpperBoundKey if End is Nil.
    52  func (s ComparableSpan) Hack() ComparableSpan {
    53  	s.Start, s.End = hackSpan(s.Start, s.End)
    54  	return s
    55  }
    56  
    57  // Clone clones a ComparableSpan
    58  func (s ComparableSpan) Clone() ComparableSpan {
    59  	return ComparableSpan{
    60  		Start: append(make([]byte, 0, len(s.Start)), s.Start...),
    61  		End:   append(make([]byte, 0, len(s.End)), s.End...),
    62  	}
    63  }
    64  
    65  // Hack will set End as UpperBoundKey if End is Nil.
    66  func (s Span) Hack() Span {
    67  	s.Start, s.End = hackSpan(s.Start, s.End)
    68  	return s
    69  }
    70  
    71  func hackSpan(originStart []byte, originEnd []byte) (start []byte, end []byte) {
    72  	start = originStart
    73  	end = originEnd
    74  
    75  	if originStart == nil {
    76  		start = []byte{}
    77  	}
    78  
    79  	if originEnd == nil {
    80  		end = UpperBoundKey
    81  	}
    82  	return
    83  }
    84  
    85  // GetTableSpan returns the span to watch for the specified table
    86  func GetTableSpan(tableID int64) Span {
    87  	sep := byte('_')
    88  	recordMarker := byte('r')
    89  	tablePrefix := tablecodec.GenTablePrefix(tableID)
    90  	var start, end kv.Key
    91  	// ignore index keys.
    92  	start = append(tablePrefix, sep, recordMarker)
    93  	end = append(tablePrefix, sep, recordMarker+1)
    94  	return Span{
    95  		Start: start,
    96  		End:   end,
    97  	}
    98  }
    99  
   100  // GetDDLSpan returns the span to watch for DDL related events
   101  func GetDDLSpan() Span {
   102  	return getMetaListKey("DDLJobList")
   103  }
   104  
   105  // GetAddIndexDDLSpan returns the span to watch for Add Index DDL related events
   106  func GetAddIndexDDLSpan() Span {
   107  	return getMetaListKey("DDLJobAddIdxList")
   108  }
   109  
   110  func getMetaListKey(key string) Span {
   111  	metaPrefix := []byte("m")
   112  	metaKey := []byte(key)
   113  	listData := 'l'
   114  	start := make([]byte, 0, len(metaPrefix)+len(metaKey)+8)
   115  	start = append(start, metaPrefix...)
   116  	start = codec.EncodeBytes(start, metaKey)
   117  	start = codec.EncodeUint(start, uint64(listData))
   118  	end := make([]byte, len(start))
   119  	copy(end, start)
   120  	end[len(end)-1]++
   121  	return Span{
   122  		Start: start,
   123  		End:   end,
   124  	}
   125  }
   126  
   127  // KeyInSpans check if k in the range of spans.
   128  func KeyInSpans(k []byte, spans []ComparableSpan) bool {
   129  	for _, span := range spans {
   130  		if KeyInSpan(k, span) {
   131  			return true
   132  		}
   133  	}
   134  
   135  	return false
   136  }
   137  
   138  // KeyInSpan check if k in the span range.
   139  func KeyInSpan(k []byte, span ComparableSpan) bool {
   140  	if StartCompare(k, span.Start) >= 0 &&
   141  		EndCompare(k, span.End) < 0 {
   142  		return true
   143  	}
   144  
   145  	return false
   146  }
   147  
   148  // StartCompare compares two start keys.
   149  // Nil means Negative infinity
   150  // The result will be 0 if lhs==rhs, -1 if lhs < rhs, and +1 if lhs > rhs
   151  func StartCompare(lhs []byte, rhs []byte) int {
   152  	if lhs == nil && rhs == nil {
   153  		return 0
   154  	}
   155  
   156  	if lhs == nil {
   157  		return -1
   158  	}
   159  
   160  	if rhs == nil {
   161  		return 1
   162  	}
   163  
   164  	return bytes.Compare(lhs, rhs)
   165  }
   166  
   167  // EndCompare compares two end keys.
   168  // Nil means Positive infinity
   169  // The result will be 0 if lhs==rhs, -1 if lhs < rhs, and +1 if lhs > rhs
   170  func EndCompare(lhs []byte, rhs []byte) int {
   171  	if lhs == nil && rhs == nil {
   172  		return 0
   173  	}
   174  
   175  	if lhs == nil {
   176  		return 1
   177  	}
   178  
   179  	if rhs == nil {
   180  		return -1
   181  	}
   182  
   183  	return bytes.Compare(lhs, rhs)
   184  }
   185  
   186  // Intersect return the intersect part of lhs and rhs span.
   187  // Return error if there's no intersect part
   188  func Intersect(lhs ComparableSpan, rhs ComparableSpan) (span ComparableSpan, err error) {
   189  	if lhs.Start != nil && EndCompare(lhs.Start, rhs.End) >= 0 ||
   190  		rhs.Start != nil && EndCompare(rhs.Start, lhs.End) >= 0 {
   191  		return ComparableSpan{}, cerror.ErrIntersectNoOverlap.GenWithStackByArgs(lhs, rhs)
   192  	}
   193  
   194  	start := lhs.Start
   195  
   196  	if StartCompare(rhs.Start, start) > 0 {
   197  		start = rhs.Start
   198  	}
   199  
   200  	end := lhs.End
   201  
   202  	if EndCompare(rhs.End, end) < 0 {
   203  		end = rhs.End
   204  	}
   205  
   206  	return ComparableSpan{Start: start, End: end}, nil
   207  }
   208  
   209  // IsSubSpan returns true if the sub span is parents spans
   210  func IsSubSpan(sub ComparableSpan, parents ...ComparableSpan) bool {
   211  	if bytes.Compare(sub.Start, sub.End) >= 0 {
   212  		log.Panic("the sub span is invalid", zap.Reflect("sub span", sub))
   213  	}
   214  	for _, parent := range parents {
   215  		if StartCompare(parent.Start, sub.Start) <= 0 &&
   216  			EndCompare(sub.End, parent.End) <= 0 {
   217  			return true
   218  		}
   219  	}
   220  	return false
   221  }
   222  
   223  // ToComparableSpan returns a memcomparable span
   224  func ToComparableSpan(span Span) ComparableSpan {
   225  	return ComparableSpan{
   226  		Start: codec.EncodeBytes(nil, span.Start),
   227  		End:   codec.EncodeBytes(nil, span.End),
   228  	}
   229  }
   230  
   231  // ToComparableKey returns a memcomparable key.
   232  func ToComparableKey(key []byte) []byte {
   233  	return codec.EncodeBytes(nil, key)
   234  }