| // Copyright 2025 The etcd Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package txn |
| |
| import ( |
| "context" |
| |
| "go.uber.org/zap" |
| |
| pb "go.etcd.io/etcd/api/v3/etcdserverpb" |
| "go.etcd.io/etcd/api/v3/mvccpb" |
| "go.etcd.io/etcd/pkg/v3/traceutil" |
| "go.etcd.io/etcd/server/v3/storage/mvcc" |
| ) |
| |
| func DeleteRange(ctx context.Context, lg *zap.Logger, kv mvcc.KV, dr *pb.DeleteRangeRequest) (resp *pb.DeleteRangeResponse, trace *traceutil.Trace, err error) { |
| ctx, trace = traceutil.EnsureTrace(ctx, lg, "delete_range", |
| traceutil.Field{Key: "key", Value: string(dr.Key)}, |
| traceutil.Field{Key: "range_end", Value: string(dr.RangeEnd)}, |
| ) |
| txnWrite := kv.Write(trace) |
| defer txnWrite.End() |
| resp, err = deleteRange(ctx, txnWrite, dr) |
| return resp, trace, err |
| } |
| |
| func deleteRange(ctx context.Context, txnWrite mvcc.TxnWrite, dr *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) { |
| resp := &pb.DeleteRangeResponse{} |
| resp.Header = &pb.ResponseHeader{} |
| end := mkGteRange(dr.RangeEnd) |
| |
| if dr.PrevKv { |
| rr, err := txnWrite.Range(ctx, dr.Key, end, mvcc.RangeOptions{}) |
| if err != nil { |
| return nil, err |
| } |
| if rr != nil { |
| resp.PrevKvs = make([]*mvccpb.KeyValue, len(rr.KVs)) |
| copy(resp.PrevKvs, rr.KVs) |
| } |
| } |
| |
| resp.Deleted, resp.Header.Revision = txnWrite.DeleteRange(dr.Key, end) |
| return resp, nil |
| } |
| |
| // mkGteRange determines if the range end is a >= range. This works around grpc |
| // sending empty byte strings as nil; >= is encoded in the range end as '\0'. |
| // If it is a GTE range, then []byte{} is returned to indicate the empty byte |
| // string (vs nil being no byte string). |
| func mkGteRange(rangeEnd []byte) []byte { |
| if len(rangeEnd) == 1 && rangeEnd[0] == 0 { |
| return []byte{} |
| } |
| return rangeEnd |
| } |