github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/pkg/meta/model/op.go (about) 1 // Copyright 2022 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // [Reference]:https://github.com/etcd-io/etcd/blob/20c89df5e5e2d738efb9c276d954d754eb86918b/client/v3/op.go 16 17 package model 18 19 import ( 20 "github.com/pingcap/tiflow/pkg/errors" 21 ) 22 23 type opType int 24 25 const ( 26 // A default Op has opType 0, which is invalid. 27 tGet opType = iota + 1 28 tPut 29 tDelete 30 tTxn 31 ) 32 33 var noPrefixEnd = []byte{0} 34 35 // Op represents an Operation that kv can execute. 36 // Support Key Range/From Key/Key Prefix attributes 37 type Op struct { 38 T opType 39 key []byte 40 end []byte 41 42 // for put 43 val []byte 44 45 // txn 46 ops []Op 47 48 isOptsWithPrefix bool 49 isOptsWithFromKey bool 50 isOptsWithRange bool 51 } 52 53 // EmptyOp creates a global empty op 54 var EmptyOp Op = Op{} 55 56 // accessors/mutators 57 58 // IsTxn returns true if the "Op" type is transaction. 59 func (op Op) IsTxn() bool { return op.T == tTxn } 60 61 // IsPut returns true if the operation is a Put. 62 func (op Op) IsPut() bool { return op.T == tPut } 63 64 // IsGet returns true if the operation is a Get. 65 func (op Op) IsGet() bool { return op.T == tGet } 66 67 // IsDelete returns true if the operation is a Delete. 68 func (op Op) IsDelete() bool { return op.T == tDelete } 69 70 // IsOptsWithPrefix returns true if WithPrefix option is called in the given opts. 71 func (op Op) IsOptsWithPrefix() bool { return op.isOptsWithPrefix } 72 73 // IsOptsWithFromKey returns true if WithFromKey option is called in the given opts. 74 func (op Op) IsOptsWithFromKey() bool { return op.isOptsWithFromKey } 75 76 // IsOptsWithRange returns true if WithRange option is called in the given opts. 77 func (op Op) IsOptsWithRange() bool { return op.isOptsWithRange } 78 79 // Txn returns the operations. 80 func (op Op) Txn() []Op { return op.ops } 81 82 // KeyBytes returns the byte slice holding the Op's key. 83 func (op Op) KeyBytes() []byte { return op.key } 84 85 // WithKeyBytes set the byte slice to the Op's key. 86 func (op *Op) WithKeyBytes(key []byte) { op.key = key } 87 88 // RangeBytes returns the byte slice holding with the Op's range end, if any. 89 func (op Op) RangeBytes() []byte { return op.end } 90 91 // WithRangeBytes set the byte slice to the Op's range end 92 func (op *Op) WithRangeBytes(end []byte) { op.end = end } 93 94 // ValueBytes returns the byte slice holding the Op's value, if any. 95 func (op Op) ValueBytes() []byte { return op.val } 96 97 // NewOp creates a new op instance 98 func NewOp() *Op { 99 return &Op{key: []byte("")} 100 } 101 102 // IsOptsWithRange returns true if WithRange option is called in the given opts. 103 func IsOptsWithRange(opts []OpOption) bool { 104 op := NewOp() 105 for _, opt := range opts { 106 opt(op) 107 } 108 109 return op.isOptsWithRange 110 } 111 112 // IsOptsWithPrefix returns true if WithPrefix option is called in the given opts. 113 func IsOptsWithPrefix(opts []OpOption) bool { 114 op := NewOp() 115 for _, opt := range opts { 116 opt(op) 117 } 118 119 return op.isOptsWithPrefix 120 } 121 122 // IsOptsWithFromKey returns true if WithFromKey option is called in the given opts. 123 func IsOptsWithFromKey(opts []OpOption) bool { 124 op := NewOp() 125 for _, opt := range opts { 126 opt(op) 127 } 128 129 return op.isOptsWithFromKey 130 } 131 132 // CheckValidOp checks whether op is valid 133 func (op Op) CheckValidOp() error { 134 // [TODO] forbit WithPrefix() + "" 135 if !(op.IsOptsWithRange() || op.IsOptsWithPrefix() || op.IsOptsWithFromKey()) { 136 return nil 137 } 138 139 if op.IsOptsWithRange() && !(op.IsOptsWithPrefix() || op.IsOptsWithFromKey()) { 140 return nil 141 } 142 if op.IsOptsWithPrefix() && !(op.IsOptsWithRange() || op.IsOptsWithFromKey()) { 143 return nil 144 } 145 if op.IsOptsWithFromKey() && !(op.IsOptsWithPrefix() || op.IsOptsWithRange()) { 146 return nil 147 } 148 return errors.ErrMetaOptionConflict.GenWithStackByArgs() 149 } 150 151 // OpGet returns "get" operation based on given key and operation options. 152 func OpGet(key string, opts ...OpOption) Op { 153 op := Op{T: tGet, key: []byte(key)} 154 op.ApplyOpts(opts) 155 return op 156 } 157 158 // OpDelete returns "delete" operation based on given key and operation options. 159 func OpDelete(key string, opts ...OpOption) Op { 160 op := Op{T: tDelete, key: []byte(key)} 161 op.ApplyOpts(opts) 162 return op 163 } 164 165 // OpPut returns "put" operation based on given key-value. 166 func OpPut(key, val string) Op { 167 op := Op{T: tPut, key: []byte(key), val: []byte(val)} 168 // [TODO]add some restriction 169 return op 170 } 171 172 // OpTxn returns "txn" operation based on given transaction conditions. 173 func OpTxn(ops []Op) Op { 174 return Op{T: tTxn, ops: ops} 175 } 176 177 // GetPrefixRangeEnd gets the range end of the prefix. 178 // 'Get(foo, WithPrefix())' is equal to 'Get(foo, WithRange(GetPrefixRangeEnd(foo))'. 179 func GetPrefixRangeEnd(prefix string) string { 180 return string(getPrefix([]byte(prefix))) 181 } 182 183 func getPrefix(key []byte) []byte { 184 end := make([]byte, len(key)) 185 copy(end, key) 186 for i := len(end) - 1; i >= 0; i-- { 187 if end[i] < 0xff { 188 end[i] = end[i] + 1 189 end = end[:i+1] 190 return end 191 } 192 } 193 // next prefix does not exist (e.g., 0xffff); 194 // default to WithFromKey policy 195 return noPrefixEnd 196 } 197 198 // ApplyOpts calls given option function one by one 199 func (op *Op) ApplyOpts(opts []OpOption) { 200 for _, opt := range opts { 201 opt(op) 202 } 203 } 204 205 // OpOption configures Operations like Get, Put, Delete. 206 type OpOption func(*Op) 207 208 // WithPrefix enables 'Get', 'Delete' requests to operate 209 // on the keys with matching prefix. For example, 'Get(foo, WithPrefix())' 210 // can return 'foo1', 'foo2', and so on. 211 func WithPrefix() OpOption { 212 return func(op *Op) { 213 if len(op.key) == 0 { 214 op.key, op.end = []byte{0}, []byte{0} 215 return 216 } 217 op.end = getPrefix(op.key) 218 op.isOptsWithPrefix = true 219 } 220 } 221 222 // WithRange specifies the range of 'Get', 'Delete' requests. 223 // For example, 'Get' requests with 'WithRange(end)' returns 224 // the keys in the range [key, end). 225 // endKey must be lexicographically greater than start key. 226 func WithRange(endKey string) OpOption { 227 return func(op *Op) { 228 op.end = []byte(endKey) 229 op.isOptsWithRange = true 230 } 231 } 232 233 // WithFromKey specifies the range of 'Get', 'Delete' requests 234 // to be equal or greater than the key in the argument. 235 func WithFromKey() OpOption { 236 return func(op *Op) { 237 if len(op.key) == 0 { 238 op.key = []byte{0} 239 } 240 op.end = []byte("\x00") 241 op.isOptsWithFromKey = true 242 } 243 }