1package miniredis
2
3import (
4	"sort"
5	"strconv"
6	"time"
7)
8
9func (db *RedisDB) exists(k string) bool {
10	_, ok := db.keys[k]
11	return ok
12}
13
14// t gives the type of a key, or ""
15func (db *RedisDB) t(k string) string {
16	return db.keys[k]
17}
18
19// allKeys returns all keys. Sorted.
20func (db *RedisDB) allKeys() []string {
21	res := make([]string, 0, len(db.keys))
22	for k := range db.keys {
23		res = append(res, k)
24	}
25	sort.Strings(res) // To make things deterministic.
26	return res
27}
28
29// flush removes all keys and values.
30func (db *RedisDB) flush() {
31	db.keys = map[string]string{}
32	db.stringKeys = map[string]string{}
33	db.hashKeys = map[string]hashKey{}
34	db.listKeys = map[string]listKey{}
35	db.setKeys = map[string]setKey{}
36	db.sortedsetKeys = map[string]sortedSet{}
37	db.ttl = map[string]time.Duration{}
38}
39
40// move something to another db. Will return ok. Or not.
41func (db *RedisDB) move(key string, to *RedisDB) bool {
42	if _, ok := to.keys[key]; ok {
43		return false
44	}
45
46	t, ok := db.keys[key]
47	if !ok {
48		return false
49	}
50	to.keys[key] = db.keys[key]
51	switch t {
52	case "string":
53		to.stringKeys[key] = db.stringKeys[key]
54	case "hash":
55		to.hashKeys[key] = db.hashKeys[key]
56	case "list":
57		to.listKeys[key] = db.listKeys[key]
58	case "set":
59		to.setKeys[key] = db.setKeys[key]
60	case "zset":
61		to.sortedsetKeys[key] = db.sortedsetKeys[key]
62	default:
63		panic("unhandled key type")
64	}
65	to.keyVersion[key]++
66	if v, ok := db.ttl[key]; ok {
67		to.ttl[key] = v
68	}
69	db.del(key, true)
70	return true
71}
72
73func (db *RedisDB) rename(from, to string) {
74	db.del(to, true)
75	switch db.t(from) {
76	case "string":
77		db.stringKeys[to] = db.stringKeys[from]
78	case "hash":
79		db.hashKeys[to] = db.hashKeys[from]
80	case "list":
81		db.listKeys[to] = db.listKeys[from]
82	case "set":
83		db.setKeys[to] = db.setKeys[from]
84	case "zset":
85		db.sortedsetKeys[to] = db.sortedsetKeys[from]
86	default:
87		panic("missing case")
88	}
89	db.keys[to] = db.keys[from]
90	db.keyVersion[to]++
91	db.ttl[to] = db.ttl[from]
92
93	db.del(from, true)
94}
95
96func (db *RedisDB) del(k string, delTTL bool) {
97	if !db.exists(k) {
98		return
99	}
100	t := db.t(k)
101	delete(db.keys, k)
102	db.keyVersion[k]++
103	if delTTL {
104		delete(db.ttl, k)
105	}
106	switch t {
107	case "string":
108		delete(db.stringKeys, k)
109	case "hash":
110		delete(db.hashKeys, k)
111	case "list":
112		delete(db.listKeys, k)
113	case "set":
114		delete(db.setKeys, k)
115	case "zset":
116		delete(db.sortedsetKeys, k)
117	default:
118		panic("Unknown key type: " + t)
119	}
120}
121
122// stringGet returns the string key or "" on error/nonexists.
123func (db *RedisDB) stringGet(k string) string {
124	if t, ok := db.keys[k]; !ok || t != "string" {
125		return ""
126	}
127	return db.stringKeys[k]
128}
129
130// stringSet force set()s a key. Does not touch expire.
131func (db *RedisDB) stringSet(k, v string) {
132	db.del(k, false)
133	db.keys[k] = "string"
134	db.stringKeys[k] = v
135	db.keyVersion[k]++
136}
137
138// change int key value
139func (db *RedisDB) stringIncr(k string, delta int) (int, error) {
140	v := 0
141	if sv, ok := db.stringKeys[k]; ok {
142		var err error
143		v, err = strconv.Atoi(sv)
144		if err != nil {
145			return 0, ErrIntValueError
146		}
147	}
148	v += delta
149	db.stringSet(k, strconv.Itoa(v))
150	return v, nil
151}
152
153// change float key value
154func (db *RedisDB) stringIncrfloat(k string, delta float64) (float64, error) {
155	v := 0.0
156	if sv, ok := db.stringKeys[k]; ok {
157		var err error
158		v, err = strconv.ParseFloat(sv, 64)
159		if err != nil {
160			return 0, ErrFloatValueError
161		}
162	}
163	v += delta
164	db.stringSet(k, formatFloat(v))
165	return v, nil
166}
167
168// listLpush is 'left push', aka unshift. Returns the new length.
169func (db *RedisDB) listLpush(k, v string) int {
170	l, ok := db.listKeys[k]
171	if !ok {
172		db.keys[k] = "list"
173	}
174	l = append([]string{v}, l...)
175	db.listKeys[k] = l
176	db.keyVersion[k]++
177	return len(l)
178}
179
180// 'left pop', aka shift.
181func (db *RedisDB) listLpop(k string) string {
182	l := db.listKeys[k]
183	el := l[0]
184	l = l[1:]
185	if len(l) == 0 {
186		db.del(k, true)
187	} else {
188		db.listKeys[k] = l
189	}
190	db.keyVersion[k]++
191	return el
192}
193
194func (db *RedisDB) listPush(k string, v ...string) int {
195	l, ok := db.listKeys[k]
196	if !ok {
197		db.keys[k] = "list"
198	}
199	l = append(l, v...)
200	db.listKeys[k] = l
201	db.keyVersion[k]++
202	return len(l)
203}
204
205func (db *RedisDB) listPop(k string) string {
206	l := db.listKeys[k]
207	el := l[len(l)-1]
208	l = l[:len(l)-1]
209	if len(l) == 0 {
210		db.del(k, true)
211	} else {
212		db.listKeys[k] = l
213		db.keyVersion[k]++
214	}
215	return el
216}
217
218// setset replaces a whole set.
219func (db *RedisDB) setSet(k string, set setKey) {
220	db.keys[k] = "set"
221	db.setKeys[k] = set
222	db.keyVersion[k]++
223}
224
225// setadd adds members to a set. Returns nr of new keys.
226func (db *RedisDB) setAdd(k string, elems ...string) int {
227	s, ok := db.setKeys[k]
228	if !ok {
229		s = setKey{}
230		db.keys[k] = "set"
231	}
232	added := 0
233	for _, e := range elems {
234		if _, ok := s[e]; !ok {
235			added++
236		}
237		s[e] = struct{}{}
238	}
239	db.setKeys[k] = s
240	db.keyVersion[k]++
241	return added
242}
243
244// setrem removes members from a set. Returns nr of deleted keys.
245func (db *RedisDB) setRem(k string, fields ...string) int {
246	s, ok := db.setKeys[k]
247	if !ok {
248		return 0
249	}
250	removed := 0
251	for _, f := range fields {
252		if _, ok := s[f]; ok {
253			removed++
254			delete(s, f)
255		}
256	}
257	if len(s) == 0 {
258		db.del(k, true)
259	} else {
260		db.setKeys[k] = s
261	}
262	db.keyVersion[k]++
263	return removed
264}
265
266// All members of a set.
267func (db *RedisDB) setMembers(k string) []string {
268	set := db.setKeys[k]
269	members := make([]string, 0, len(set))
270	for k := range set {
271		members = append(members, k)
272	}
273	sort.Strings(members)
274	return members
275}
276
277// Is a SET value present?
278func (db *RedisDB) setIsMember(k, v string) bool {
279	set, ok := db.setKeys[k]
280	if !ok {
281		return false
282	}
283	_, ok = set[v]
284	return ok
285}
286
287// hashFields returns all (sorted) keys ('fields') for a hash key.
288func (db *RedisDB) hashFields(k string) []string {
289	v := db.hashKeys[k]
290	r := make([]string, 0, len(v))
291	for k := range v {
292		r = append(r, k)
293	}
294	sort.Strings(r)
295	return r
296}
297
298// hashGet a value
299func (db *RedisDB) hashGet(key, field string) string {
300	return db.hashKeys[key][field]
301}
302
303// hashSet returns whether the key already existed
304func (db *RedisDB) hashSet(k, f, v string) bool {
305	if t, ok := db.keys[k]; ok && t != "hash" {
306		db.del(k, true)
307	}
308	db.keys[k] = "hash"
309	if _, ok := db.hashKeys[k]; !ok {
310		db.hashKeys[k] = map[string]string{}
311	}
312	_, ok := db.hashKeys[k][f]
313	db.hashKeys[k][f] = v
314	db.keyVersion[k]++
315	return ok
316}
317
318// hashIncr changes int key value
319func (db *RedisDB) hashIncr(key, field string, delta int) (int, error) {
320	v := 0
321	if h, ok := db.hashKeys[key]; ok {
322		if f, ok := h[field]; ok {
323			var err error
324			v, err = strconv.Atoi(f)
325			if err != nil {
326				return 0, ErrIntValueError
327			}
328		}
329	}
330	v += delta
331	db.hashSet(key, field, strconv.Itoa(v))
332	return v, nil
333}
334
335// hashIncrfloat changes float key value
336func (db *RedisDB) hashIncrfloat(key, field string, delta float64) (float64, error) {
337	v := 0.0
338	if h, ok := db.hashKeys[key]; ok {
339		if f, ok := h[field]; ok {
340			var err error
341			v, err = strconv.ParseFloat(f, 64)
342			if err != nil {
343				return 0, ErrFloatValueError
344			}
345		}
346	}
347	v += delta
348	db.hashSet(key, field, formatFloat(v))
349	return v, nil
350}
351
352// sortedSet set returns a sortedSet as map
353func (db *RedisDB) sortedSet(key string) map[string]float64 {
354	ss := db.sortedsetKeys[key]
355	return map[string]float64(ss)
356}
357
358// ssetSet sets a complete sorted set.
359func (db *RedisDB) ssetSet(key string, sset sortedSet) {
360	db.keys[key] = "zset"
361	db.keyVersion[key]++
362	db.sortedsetKeys[key] = sset
363}
364
365// ssetAdd adds member to a sorted set. Returns whether this was a new member.
366func (db *RedisDB) ssetAdd(key string, score float64, member string) bool {
367	ss, ok := db.sortedsetKeys[key]
368	if !ok {
369		ss = newSortedSet()
370		db.keys[key] = "zset"
371	}
372	_, ok = ss[member]
373	ss[member] = score
374	db.sortedsetKeys[key] = ss
375	db.keyVersion[key]++
376	return !ok
377}
378
379// All members from a sorted set, ordered by score.
380func (db *RedisDB) ssetMembers(key string) []string {
381	ss, ok := db.sortedsetKeys[key]
382	if !ok {
383		return nil
384	}
385	elems := ss.byScore(asc)
386	members := make([]string, 0, len(elems))
387	for _, e := range elems {
388		members = append(members, e.member)
389	}
390	return members
391}
392
393// All members+scores from a sorted set, ordered by score.
394func (db *RedisDB) ssetElements(key string) ssElems {
395	ss, ok := db.sortedsetKeys[key]
396	if !ok {
397		return nil
398	}
399	return ss.byScore(asc)
400}
401
402// ssetCard is the sorted set cardinality.
403func (db *RedisDB) ssetCard(key string) int {
404	ss := db.sortedsetKeys[key]
405	return ss.card()
406}
407
408// ssetRank is the sorted set rank.
409func (db *RedisDB) ssetRank(key, member string, d direction) (int, bool) {
410	ss := db.sortedsetKeys[key]
411	return ss.rankByScore(member, d)
412}
413
414// ssetScore is sorted set score.
415func (db *RedisDB) ssetScore(key, member string) float64 {
416	ss := db.sortedsetKeys[key]
417	return ss[member]
418}
419
420// ssetRem is sorted set key delete.
421func (db *RedisDB) ssetRem(key, member string) bool {
422	ss := db.sortedsetKeys[key]
423	_, ok := ss[member]
424	delete(ss, member)
425	if len(ss) == 0 {
426		// Delete key on removal of last member
427		db.del(key, true)
428	}
429	return ok
430}
431
432// ssetExists tells if a member exists in a sorted set.
433func (db *RedisDB) ssetExists(key, member string) bool {
434	ss := db.sortedsetKeys[key]
435	_, ok := ss[member]
436	return ok
437}
438
439// ssetIncrby changes float sorted set score.
440func (db *RedisDB) ssetIncrby(k, m string, delta float64) float64 {
441	ss, ok := db.sortedsetKeys[k]
442	if !ok {
443		ss = newSortedSet()
444		db.keys[k] = "zset"
445		db.sortedsetKeys[k] = ss
446	}
447
448	v, _ := ss.get(m)
449	v += delta
450	ss.set(v, m)
451	db.keyVersion[k]++
452	return v
453}
454
455// setDiff implements the logic behind SDIFF*
456func (db *RedisDB) setDiff(keys []string) (setKey, error) {
457	key := keys[0]
458	keys = keys[1:]
459	if db.exists(key) && db.t(key) != "set" {
460		return nil, ErrWrongType
461	}
462	s := setKey{}
463	for k := range db.setKeys[key] {
464		s[k] = struct{}{}
465	}
466	for _, sk := range keys {
467		if !db.exists(sk) {
468			continue
469		}
470		if db.t(sk) != "set" {
471			return nil, ErrWrongType
472		}
473		for e := range db.setKeys[sk] {
474			delete(s, e)
475		}
476	}
477	return s, nil
478}
479
480// setInter implements the logic behind SINTER*
481func (db *RedisDB) setInter(keys []string) (setKey, error) {
482	key := keys[0]
483	keys = keys[1:]
484	if !db.exists(key) {
485		return setKey{}, nil
486	}
487	if db.t(key) != "set" {
488		return nil, ErrWrongType
489	}
490	s := setKey{}
491	for k := range db.setKeys[key] {
492		s[k] = struct{}{}
493	}
494	for _, sk := range keys {
495		if !db.exists(sk) {
496			return setKey{}, nil
497		}
498		if db.t(sk) != "set" {
499			return nil, ErrWrongType
500		}
501		other := db.setKeys[sk]
502		for e := range s {
503			if _, ok := other[e]; ok {
504				continue
505			}
506			delete(s, e)
507		}
508	}
509	return s, nil
510}
511
512// setUnion implements the logic behind SUNION*
513func (db *RedisDB) setUnion(keys []string) (setKey, error) {
514	key := keys[0]
515	keys = keys[1:]
516	if db.exists(key) && db.t(key) != "set" {
517		return nil, ErrWrongType
518	}
519	s := setKey{}
520	for k := range db.setKeys[key] {
521		s[k] = struct{}{}
522	}
523	for _, sk := range keys {
524		if !db.exists(sk) {
525			continue
526		}
527		if db.t(sk) != "set" {
528			return nil, ErrWrongType
529		}
530		for e := range db.setKeys[sk] {
531			s[e] = struct{}{}
532		}
533	}
534	return s, nil
535}
536
537// fastForward proceeds the current timestamp with duration, works as a time machine
538func (db *RedisDB) fastForward(duration time.Duration) {
539	for _, key := range db.allKeys() {
540		if value, ok := db.ttl[key]; ok {
541			db.ttl[key] = value - duration
542			db.checkTTL(key)
543		}
544	}
545}
546
547func (db *RedisDB) checkTTL(key string) {
548	if v, ok := db.ttl[key]; ok && v <= 0 {
549		db.del(key, true)
550	}
551}
552