• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

HDT3213 / godis / 15238419529

25 May 2025 01:32PM UTC coverage: 72.019% (-3.7%) from 75.704%
15238419529

push

github

HDT3213
update github actions go version

1 of 1 new or added line in 1 file covered. (100.0%)

1149 existing lines in 29 files now uncovered.

8473 of 11765 relevant lines covered (72.02%)

0.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.79
/database/set.go
1
package database
2

3
import (
4
        HashSet "github.com/hdt3213/godis/datastruct/set"
5
        "github.com/hdt3213/godis/interface/database"
6
        "github.com/hdt3213/godis/interface/redis"
7
        "github.com/hdt3213/godis/lib/utils"
8
        "github.com/hdt3213/godis/redis/protocol"
9
        "strconv"
10
        "strings"
11
)
12

13
func (db *DB) getAsSet(key string) (*HashSet.Set, protocol.ErrorReply) {
1✔
14
        entity, exists := db.GetEntity(key)
1✔
15
        if !exists {
2✔
16
                return nil, nil
1✔
17
        }
1✔
18
        set, ok := entity.Data.(*HashSet.Set)
1✔
19
        if !ok {
1✔
20
                return nil, &protocol.WrongTypeErrReply{}
×
UNCOV
21
        }
×
22
        return set, nil
1✔
23
}
24

25
func (db *DB) getOrInitSet(key string) (set *HashSet.Set, inited bool, errReply protocol.ErrorReply) {
1✔
26
        set, errReply = db.getAsSet(key)
1✔
27
        if errReply != nil {
1✔
28
                return nil, false, errReply
×
UNCOV
29
        }
×
30
        inited = false
1✔
31
        if set == nil {
2✔
32
                set = HashSet.Make()
1✔
33
                db.PutEntity(key, &database.DataEntity{
1✔
34
                        Data: set,
1✔
35
                })
1✔
36
                inited = true
1✔
37
        }
1✔
38
        return set, inited, nil
1✔
39
}
40

41
// execSAdd adds members into set
42
func execSAdd(db *DB, args [][]byte) redis.Reply {
1✔
43
        key := string(args[0])
1✔
44
        members := args[1:]
1✔
45

1✔
46
        // get or init entity
1✔
47
        set, _, errReply := db.getOrInitSet(key)
1✔
48
        if errReply != nil {
1✔
49
                return errReply
×
UNCOV
50
        }
×
51
        counter := 0
1✔
52
        for _, member := range members {
2✔
53
                counter += set.Add(string(member))
1✔
54
        }
1✔
55
        db.addAof(utils.ToCmdLine3("sadd", args...))
1✔
56
        return protocol.MakeIntReply(int64(counter))
1✔
57
}
58

59
// execSIsMember checks if the given value is member of set
60
func execSIsMember(db *DB, args [][]byte) redis.Reply {
1✔
61
        key := string(args[0])
1✔
62
        member := string(args[1])
1✔
63

1✔
64
        // get set
1✔
65
        set, errReply := db.getAsSet(key)
1✔
66
        if errReply != nil {
1✔
67
                return errReply
×
UNCOV
68
        }
×
69
        if set == nil {
2✔
70
                return protocol.MakeIntReply(0)
1✔
71
        }
1✔
72

73
        has := set.Has(member)
1✔
74
        if has {
2✔
75
                return protocol.MakeIntReply(1)
1✔
76
        }
1✔
77
        return protocol.MakeIntReply(0)
1✔
78
}
79

80
// execSRem removes a member from set
81
func execSRem(db *DB, args [][]byte) redis.Reply {
1✔
82
        key := string(args[0])
1✔
83
        members := args[1:]
1✔
84

1✔
85
        set, errReply := db.getAsSet(key)
1✔
86
        if errReply != nil {
1✔
87
                return errReply
×
UNCOV
88
        }
×
89
        if set == nil {
1✔
90
                return protocol.MakeIntReply(0)
×
UNCOV
91
        }
×
92
        counter := 0
1✔
93
        for _, member := range members {
2✔
94
                counter += set.Remove(string(member))
1✔
95
        }
1✔
96
        if set.Len() == 0 {
2✔
97
                db.Remove(key)
1✔
98
        }
1✔
99
        if counter > 0 {
2✔
100
                db.addAof(utils.ToCmdLine3("srem", args...))
1✔
101
        }
1✔
102
        return protocol.MakeIntReply(int64(counter))
1✔
103
}
104

105
// execSPop removes one or more random members from set
106
func execSPop(db *DB, args [][]byte) redis.Reply {
1✔
107
        if len(args) != 1 && len(args) != 2 {
1✔
108
                return protocol.MakeErrReply("ERR wrong number of arguments for 'spop' command")
×
UNCOV
109
        }
×
110
        key := string(args[0])
1✔
111

1✔
112
        set, errReply := db.getAsSet(key)
1✔
113
        if errReply != nil {
1✔
114
                return errReply
×
UNCOV
115
        }
×
116
        if set == nil {
1✔
117
                return &protocol.NullBulkReply{}
×
UNCOV
118
        }
×
119

120
        count := 1
1✔
121
        if len(args) == 2 {
2✔
122
                count64, err := strconv.ParseInt(string(args[1]), 10, 64)
1✔
123
                if err != nil || count64 <= 0 {
1✔
124
                        return protocol.MakeErrReply("ERR value is out of range, must be positive")
×
UNCOV
125
                }
×
126
                count = int(count64)
1✔
127
        }
128
        if count > set.Len() {
1✔
129
                count = set.Len()
×
UNCOV
130
        }
×
131

132
        members := set.RandomDistinctMembers(count)
1✔
133
        result := make([][]byte, len(members))
1✔
134
        for i, v := range members {
2✔
135
                set.Remove(v)
1✔
136
                result[i] = []byte(v)
1✔
137
        }
1✔
138

139
        if count > 0 {
2✔
140
                db.addAof(utils.ToCmdLine3("spop", args...))
1✔
141
        }
1✔
142
        return protocol.MakeMultiBulkReply(result)
1✔
143
}
144

145
// execSCard gets the number of members in a set
146
func execSCard(db *DB, args [][]byte) redis.Reply {
1✔
147
        key := string(args[0])
1✔
148

1✔
149
        // get or init entity
1✔
150
        set, errReply := db.getAsSet(key)
1✔
151
        if errReply != nil {
1✔
152
                return errReply
×
UNCOV
153
        }
×
154
        if set == nil {
1✔
155
                return protocol.MakeIntReply(0)
×
UNCOV
156
        }
×
157
        return protocol.MakeIntReply(int64(set.Len()))
1✔
158
}
159

160
// execSMembers gets all members in a set
161
func execSMembers(db *DB, args [][]byte) redis.Reply {
1✔
162
        key := string(args[0])
1✔
163

1✔
164
        // get or init entity
1✔
165
        set, errReply := db.getAsSet(key)
1✔
166
        if errReply != nil {
1✔
167
                return errReply
×
UNCOV
168
        }
×
169
        if set == nil {
1✔
170
                return &protocol.EmptyMultiBulkReply{}
×
UNCOV
171
        }
×
172

173
        arr := make([][]byte, set.Len())
1✔
174
        i := 0
1✔
175
        set.ForEach(func(member string) bool {
2✔
176
                arr[i] = []byte(member)
1✔
177
                i++
1✔
178
                return true
1✔
179
        })
1✔
180
        return protocol.MakeMultiBulkReply(arr)
1✔
181
}
182

183
func set2reply(set *HashSet.Set) redis.Reply {
1✔
184
        arr := make([][]byte, set.Len())
1✔
185
        i := 0
1✔
186
        set.ForEach(func(member string) bool {
2✔
187
                arr[i] = []byte(member)
1✔
188
                i++
1✔
189
                return true
1✔
190
        })
1✔
191
        return protocol.MakeMultiBulkReply(arr)
1✔
192
}
193

194
// execSInter intersect multiple sets
195
func execSInter(db *DB, args [][]byte) redis.Reply {
1✔
196
        sets := make([]*HashSet.Set, 0, len(args))
1✔
197
        for _, arg := range args {
2✔
198
                key := string(arg)
1✔
199
                set, errReply := db.getAsSet(key)
1✔
200
                if errReply != nil {
1✔
UNCOV
201
                        return errReply
×
UNCOV
202
                }
×
203
                if set.Len() == 0 {
2✔
204
                        return &protocol.EmptyMultiBulkReply{}
1✔
205
                }
1✔
206
                sets = append(sets, set)
1✔
207
        }
208
        result := HashSet.Intersect(sets...)
1✔
209
        return set2reply(result)
1✔
210
}
211

212
// execSInterStore intersects multiple sets and store the result in a key
213
func execSInterStore(db *DB, args [][]byte) redis.Reply {
1✔
214
        dest := string(args[0])
1✔
215
        sets := make([]*HashSet.Set, 0, len(args)-1)
1✔
216
        for i := 1; i < len(args); i++ {
2✔
217
                key := string(args[i])
1✔
218
                set, errReply := db.getAsSet(key)
1✔
219
                if errReply != nil {
1✔
UNCOV
220
                        return errReply
×
UNCOV
221
                }
×
222
                if set.Len() == 0 {
2✔
223
                        return protocol.MakeIntReply(0)
1✔
224
                }
1✔
225
                sets = append(sets, set)
1✔
226
        }
227
        result := HashSet.Intersect(sets...)
1✔
228

1✔
229
        db.PutEntity(dest, &database.DataEntity{
1✔
230
                Data: result,
1✔
231
        })
1✔
232
        db.addAof(utils.ToCmdLine3("sinterstore", args...))
1✔
233
        return protocol.MakeIntReply(int64(result.Len()))
1✔
234
}
235

236
// execSUnion adds multiple sets
237
func execSUnion(db *DB, args [][]byte) redis.Reply {
1✔
238
        sets := make([]*HashSet.Set, 0, len(args))
1✔
239
        for _, arg := range args {
2✔
240
                key := string(arg)
1✔
241
                set, errReply := db.getAsSet(key)
1✔
242
                if errReply != nil {
1✔
UNCOV
243
                        return errReply
×
UNCOV
244
                }
×
245
                sets = append(sets, set)
1✔
246
        }
247
        result := HashSet.Union(sets...)
1✔
248
        return set2reply(result)
1✔
249
}
250

251
// execSUnionStore adds multiple sets and store the result in a key
252
func execSUnionStore(db *DB, args [][]byte) redis.Reply {
1✔
253
        dest := string(args[0])
1✔
254
        sets := make([]*HashSet.Set, 0, len(args)-1)
1✔
255
        for i := 1; i < len(args); i++ {
2✔
256
                key := string(args[i])
1✔
257
                set, errReply := db.getAsSet(key)
1✔
258
                if errReply != nil {
1✔
UNCOV
259
                        return errReply
×
UNCOV
260
                }
×
261
                sets = append(sets, set)
1✔
262
        }
263
        result := HashSet.Union(sets...)
1✔
264
        db.Remove(dest) // clean ttl
1✔
265
        if result.Len() == 0 {
1✔
UNCOV
266
                return protocol.MakeIntReply(0)
×
UNCOV
267
        }
×
268

269
        db.PutEntity(dest, &database.DataEntity{
1✔
270
                Data: result,
1✔
271
        })
1✔
272
        db.addAof(utils.ToCmdLine3("sunionstore", args...))
1✔
273
        return protocol.MakeIntReply(int64(result.Len()))
1✔
274
}
275

276
// execSDiff subtracts multiple sets
277
func execSDiff(db *DB, args [][]byte) redis.Reply {
1✔
278
        sets := make([]*HashSet.Set, 0, len(args))
1✔
279
        for _, arg := range args {
2✔
280
                key := string(arg)
1✔
281
                set, errReply := db.getAsSet(key)
1✔
282
                if errReply != nil {
1✔
UNCOV
283
                        return errReply
×
UNCOV
284
                }
×
285
                sets = append(sets, set)
1✔
286
        }
287
        result := HashSet.Diff(sets...)
1✔
288
        return set2reply(result)
1✔
289
}
290

291
// execSDiffStore subtracts multiple sets and store the result in a key
292
func execSDiffStore(db *DB, args [][]byte) redis.Reply {
1✔
293
        dest := string(args[0])
1✔
294
        sets := make([]*HashSet.Set, 0, len(args)-1)
1✔
295
        for i := 1; i < len(args); i++ {
2✔
296
                key := string(args[i])
1✔
297
                set, errReply := db.getAsSet(key)
1✔
298
                if errReply != nil {
1✔
UNCOV
299
                        return errReply
×
UNCOV
300
                }
×
301
                sets = append(sets, set)
1✔
302
        }
303
        result := HashSet.Diff(sets...)
1✔
304
        db.Remove(dest) // clean ttl
1✔
305
        if result.Len() == 0 {
2✔
306
                return protocol.MakeIntReply(0)
1✔
307
        }
1✔
308
        db.PutEntity(dest, &database.DataEntity{
1✔
309
                Data: result,
1✔
310
        })
1✔
311
        db.addAof(utils.ToCmdLine3("sdiffstore", args...))
1✔
312
        return protocol.MakeIntReply(int64(result.Len()))
1✔
313
}
314

315
// execSRandMember gets random members from set
316
func execSRandMember(db *DB, args [][]byte) redis.Reply {
1✔
317
        if len(args) != 1 && len(args) != 2 {
1✔
UNCOV
318
                return protocol.MakeErrReply("ERR wrong number of arguments for 'srandmember' command")
×
UNCOV
319
        }
×
320
        key := string(args[0])
1✔
321

1✔
322
        // get or init entity
1✔
323
        set, errReply := db.getAsSet(key)
1✔
324
        if errReply != nil {
1✔
UNCOV
325
                return errReply
×
UNCOV
326
        }
×
327
        if set == nil {
1✔
UNCOV
328
                return &protocol.NullBulkReply{}
×
329
        }
×
330
        if len(args) == 1 {
2✔
331
                // get a random member
1✔
332
                members := set.RandomMembers(1)
1✔
333
                return protocol.MakeBulkReply([]byte(members[0]))
1✔
334
        }
1✔
335
        count64, err := strconv.ParseInt(string(args[1]), 10, 64)
1✔
336
        if err != nil {
1✔
UNCOV
337
                return protocol.MakeErrReply("ERR value is not an integer or out of range")
×
UNCOV
338
        }
×
339
        count := int(count64)
1✔
340
        if count > 0 {
2✔
341
                members := set.RandomDistinctMembers(count)
1✔
342
                result := make([][]byte, len(members))
1✔
343
                for i, v := range members {
2✔
344
                        result[i] = []byte(v)
1✔
345
                }
1✔
346
                return protocol.MakeMultiBulkReply(result)
1✔
347
        } else if count < 0 {
2✔
348
                members := set.RandomMembers(-count)
1✔
349
                result := make([][]byte, len(members))
1✔
350
                for i, v := range members {
2✔
351
                        result[i] = []byte(v)
1✔
352
                }
1✔
353
                return protocol.MakeMultiBulkReply(result)
1✔
354
        }
UNCOV
355
        return &protocol.EmptyMultiBulkReply{}
×
356
}
357

358
func execSScan(db *DB, args [][]byte) redis.Reply {
1✔
359
        var count int = 10
1✔
360
        var pattern string = "*"
1✔
361
        if len(args) > 2 {
2✔
362
                for i := 2; i < len(args); i++ {
2✔
363
                        arg := strings.ToLower(string(args[i]))
1✔
364
                        if arg == "count" {
2✔
365
                                count0, err := strconv.Atoi(string(args[i+1]))
1✔
366
                                if err != nil {
1✔
UNCOV
367
                                        return &protocol.SyntaxErrReply{}
×
UNCOV
368
                                }
×
369
                                count = count0
1✔
370
                                i++
1✔
371
                        } else if arg == "match" {
2✔
372
                                pattern = string(args[i+1])
1✔
373
                                i++
1✔
374
                        } else {
1✔
375
                                return &protocol.SyntaxErrReply{}
×
376
                        }
×
377
                }
378
        }
379
        key := string(args[0])
1✔
380
        // get entity
1✔
381
        set, errReply := db.getAsSet(key)
1✔
382
        if errReply != nil {
1✔
UNCOV
383
                return errReply
×
UNCOV
384
        }
×
385
        if set == nil {
1✔
UNCOV
386
                return &protocol.EmptyMultiBulkReply{}
×
UNCOV
387
        }
×
388
        cursor, err := strconv.Atoi(string(args[1]))
1✔
389
        if err != nil {
1✔
UNCOV
390
                return protocol.MakeErrReply("ERR invalid cursor")
×
UNCOV
391
        }
×
392

393
        keysReply, nextCursor := set.SetScan(cursor, count, pattern)
1✔
394
        if nextCursor < 0 {
1✔
UNCOV
395
                return protocol.MakeErrReply("Invalid argument")
×
UNCOV
396
        }
×
397

398
        result := make([]redis.Reply, 2)
1✔
399
        result[0] = protocol.MakeBulkReply([]byte(strconv.FormatInt(int64(nextCursor), 10)))
1✔
400
        result[1] = protocol.MakeMultiBulkReply(keysReply)
1✔
401

1✔
402
        return protocol.MakeMultiRawReply(result)
1✔
403
}
404

405
func init() {
1✔
406
        registerCommand("SAdd", execSAdd, writeFirstKey, undoSetChange, -3, flagWrite).
1✔
407
                attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1)
1✔
408
        registerCommand("SIsMember", execSIsMember, readFirstKey, nil, 3, flagReadOnly).
1✔
409
                attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1)
1✔
410
        registerCommand("SRem", execSRem, writeFirstKey, undoSetChange, -3, flagWrite).
1✔
411
                attachCommandExtra([]string{redisFlagWrite, redisFlagFast}, 1, 1, 1)
1✔
412
        registerCommand("SPop", execSPop, writeFirstKey, undoSetChange, -2, flagWrite).
1✔
413
                attachCommandExtra([]string{redisFlagWrite, redisFlagRandom, redisFlagFast}, 1, 1, 1)
1✔
414
        registerCommand("SCard", execSCard, readFirstKey, nil, 2, flagReadOnly).
1✔
415
                attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1)
1✔
416
        registerCommand("SMembers", execSMembers, readFirstKey, nil, 2, flagReadOnly).
1✔
417
                attachCommandExtra([]string{redisFlagReadonly, redisFlagSortForScript}, 1, 1, 1)
1✔
418
        registerCommand("SInter", execSInter, prepareSetCalculate, nil, -2, flagReadOnly).
1✔
419
                attachCommandExtra([]string{redisFlagReadonly, redisFlagSortForScript}, 1, -1, 1)
1✔
420
        registerCommand("SInterStore", execSInterStore, prepareSetCalculateStore, rollbackFirstKey, -3, flagWrite).
1✔
421
                attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM}, 1, -1, 1)
1✔
422
        registerCommand("SUnion", execSUnion, prepareSetCalculate, nil, -2, flagReadOnly).
1✔
423
                attachCommandExtra([]string{redisFlagReadonly, redisFlagSortForScript}, 1, -1, 1)
1✔
424
        registerCommand("SUnionStore", execSUnionStore, prepareSetCalculateStore, rollbackFirstKey, -3, flagWrite).
1✔
425
                attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM}, 1, -1, 1)
1✔
426
        registerCommand("SDiff", execSDiff, prepareSetCalculate, nil, -2, flagReadOnly).
1✔
427
                attachCommandExtra([]string{redisFlagReadonly, redisFlagSortForScript}, 1, 1, 1)
1✔
428
        registerCommand("SDiffStore", execSDiffStore, prepareSetCalculateStore, rollbackFirstKey, -3, flagWrite).
1✔
429
                attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM}, 1, 1, 1)
1✔
430
        registerCommand("SRandMember", execSRandMember, readFirstKey, nil, -2, flagReadOnly).
1✔
431
                attachCommandExtra([]string{redisFlagReadonly, redisFlagRandom}, 1, 1, 1)
1✔
432
        registerCommand("SScan", execSScan, readFirstKey, nil, -2, flagReadOnly).
1✔
433
                attachCommandExtra([]string{redisFlagReadonly, redisFlagSortForScript}, 1, 1, 1)
1✔
434
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc