xref: /redis-3.2.3/tests/unit/type/hash.tcl (revision 846da5b2)
1start_server {tags {"hash"}} {
2    test {HSET/HLEN - Small hash creation} {
3        array set smallhash {}
4        for {set i 0} {$i < 8} {incr i} {
5            set key __avoid_collisions__[randstring 0 8 alpha]
6            set val __avoid_collisions__[randstring 0 8 alpha]
7            if {[info exists smallhash($key)]} {
8                incr i -1
9                continue
10            }
11            r hset smallhash $key $val
12            set smallhash($key) $val
13        }
14        list [r hlen smallhash]
15    } {8}
16
17    test {Is the small hash encoded with a ziplist?} {
18        assert_encoding ziplist smallhash
19    }
20
21    test {HSET/HLEN - Big hash creation} {
22        array set bighash {}
23        for {set i 0} {$i < 1024} {incr i} {
24            set key __avoid_collisions__[randstring 0 8 alpha]
25            set val __avoid_collisions__[randstring 0 8 alpha]
26            if {[info exists bighash($key)]} {
27                incr i -1
28                continue
29            }
30            r hset bighash $key $val
31            set bighash($key) $val
32        }
33        list [r hlen bighash]
34    } {1024}
35
36    test {Is the big hash encoded with an hash table?} {
37        assert_encoding hashtable bighash
38    }
39
40    test {HGET against the small hash} {
41        set err {}
42        foreach k [array names smallhash *] {
43            if {$smallhash($k) ne [r hget smallhash $k]} {
44                set err "$smallhash($k) != [r hget smallhash $k]"
45                break
46            }
47        }
48        set _ $err
49    } {}
50
51    test {HGET against the big hash} {
52        set err {}
53        foreach k [array names bighash *] {
54            if {$bighash($k) ne [r hget bighash $k]} {
55                set err "$bighash($k) != [r hget bighash $k]"
56                break
57            }
58        }
59        set _ $err
60    } {}
61
62    test {HGET against non existing key} {
63        set rv {}
64        lappend rv [r hget smallhash __123123123__]
65        lappend rv [r hget bighash __123123123__]
66        set _ $rv
67    } {{} {}}
68
69    test {HSET in update and insert mode} {
70        set rv {}
71        set k [lindex [array names smallhash *] 0]
72        lappend rv [r hset smallhash $k newval1]
73        set smallhash($k) newval1
74        lappend rv [r hget smallhash $k]
75        lappend rv [r hset smallhash __foobar123__ newval]
76        set k [lindex [array names bighash *] 0]
77        lappend rv [r hset bighash $k newval2]
78        set bighash($k) newval2
79        lappend rv [r hget bighash $k]
80        lappend rv [r hset bighash __foobar123__ newval]
81        lappend rv [r hdel smallhash __foobar123__]
82        lappend rv [r hdel bighash __foobar123__]
83        set _ $rv
84    } {0 newval1 1 0 newval2 1 1 1}
85
86    test {HSETNX target key missing - small hash} {
87        r hsetnx smallhash __123123123__ foo
88        r hget smallhash __123123123__
89    } {foo}
90
91    test {HSETNX target key exists - small hash} {
92        r hsetnx smallhash __123123123__ bar
93        set result [r hget smallhash __123123123__]
94        r hdel smallhash __123123123__
95        set _ $result
96    } {foo}
97
98    test {HSETNX target key missing - big hash} {
99        r hsetnx bighash __123123123__ foo
100        r hget bighash __123123123__
101    } {foo}
102
103    test {HSETNX target key exists - big hash} {
104        r hsetnx bighash __123123123__ bar
105        set result [r hget bighash __123123123__]
106        r hdel bighash __123123123__
107        set _ $result
108    } {foo}
109
110    test {HMSET wrong number of args} {
111        catch {r hmset smallhash key1 val1 key2} err
112        format $err
113    } {*wrong number*}
114
115    test {HMSET - small hash} {
116        set args {}
117        foreach {k v} [array get smallhash] {
118            set newval [randstring 0 8 alpha]
119            set smallhash($k) $newval
120            lappend args $k $newval
121        }
122        r hmset smallhash {*}$args
123    } {OK}
124
125    test {HMSET - big hash} {
126        set args {}
127        foreach {k v} [array get bighash] {
128            set newval [randstring 0 8 alpha]
129            set bighash($k) $newval
130            lappend args $k $newval
131        }
132        r hmset bighash {*}$args
133    } {OK}
134
135    test {HMGET against non existing key and fields} {
136        set rv {}
137        lappend rv [r hmget doesntexist __123123123__ __456456456__]
138        lappend rv [r hmget smallhash __123123123__ __456456456__]
139        lappend rv [r hmget bighash __123123123__ __456456456__]
140        set _ $rv
141    } {{{} {}} {{} {}} {{} {}}}
142
143    test {HMGET against wrong type} {
144        r set wrongtype somevalue
145        assert_error "*wrong*" {r hmget wrongtype field1 field2}
146    }
147
148    test {HMGET - small hash} {
149        set keys {}
150        set vals {}
151        foreach {k v} [array get smallhash] {
152            lappend keys $k
153            lappend vals $v
154        }
155        set err {}
156        set result [r hmget smallhash {*}$keys]
157        if {$vals ne $result} {
158            set err "$vals != $result"
159            break
160        }
161        set _ $err
162    } {}
163
164    test {HMGET - big hash} {
165        set keys {}
166        set vals {}
167        foreach {k v} [array get bighash] {
168            lappend keys $k
169            lappend vals $v
170        }
171        set err {}
172        set result [r hmget bighash {*}$keys]
173        if {$vals ne $result} {
174            set err "$vals != $result"
175            break
176        }
177        set _ $err
178    } {}
179
180    test {HKEYS - small hash} {
181        lsort [r hkeys smallhash]
182    } [lsort [array names smallhash *]]
183
184    test {HKEYS - big hash} {
185        lsort [r hkeys bighash]
186    } [lsort [array names bighash *]]
187
188    test {HVALS - small hash} {
189        set vals {}
190        foreach {k v} [array get smallhash] {
191            lappend vals $v
192        }
193        set _ [lsort $vals]
194    } [lsort [r hvals smallhash]]
195
196    test {HVALS - big hash} {
197        set vals {}
198        foreach {k v} [array get bighash] {
199            lappend vals $v
200        }
201        set _ [lsort $vals]
202    } [lsort [r hvals bighash]]
203
204    test {HGETALL - small hash} {
205        lsort [r hgetall smallhash]
206    } [lsort [array get smallhash]]
207
208    test {HGETALL - big hash} {
209        lsort [r hgetall bighash]
210    } [lsort [array get bighash]]
211
212    test {HDEL and return value} {
213        set rv {}
214        lappend rv [r hdel smallhash nokey]
215        lappend rv [r hdel bighash nokey]
216        set k [lindex [array names smallhash *] 0]
217        lappend rv [r hdel smallhash $k]
218        lappend rv [r hdel smallhash $k]
219        lappend rv [r hget smallhash $k]
220        unset smallhash($k)
221        set k [lindex [array names bighash *] 0]
222        lappend rv [r hdel bighash $k]
223        lappend rv [r hdel bighash $k]
224        lappend rv [r hget bighash $k]
225        unset bighash($k)
226        set _ $rv
227    } {0 0 1 0 {} 1 0 {}}
228
229    test {HDEL - more than a single value} {
230        set rv {}
231        r del myhash
232        r hmset myhash a 1 b 2 c 3
233        assert_equal 0 [r hdel myhash x y]
234        assert_equal 2 [r hdel myhash a c f]
235        r hgetall myhash
236    } {b 2}
237
238    test {HDEL - hash becomes empty before deleting all specified fields} {
239        r del myhash
240        r hmset myhash a 1 b 2 c 3
241        assert_equal 3 [r hdel myhash a b c d e]
242        assert_equal 0 [r exists myhash]
243    }
244
245    test {HEXISTS} {
246        set rv {}
247        set k [lindex [array names smallhash *] 0]
248        lappend rv [r hexists smallhash $k]
249        lappend rv [r hexists smallhash nokey]
250        set k [lindex [array names bighash *] 0]
251        lappend rv [r hexists bighash $k]
252        lappend rv [r hexists bighash nokey]
253    } {1 0 1 0}
254
255    test {Is a ziplist encoded Hash promoted on big payload?} {
256        r hset smallhash foo [string repeat a 1024]
257        r debug object smallhash
258    } {*hashtable*}
259
260    test {HINCRBY against non existing database key} {
261        r del htest
262        list [r hincrby htest foo 2]
263    } {2}
264
265    test {HINCRBY against non existing hash key} {
266        set rv {}
267        r hdel smallhash tmp
268        r hdel bighash tmp
269        lappend rv [r hincrby smallhash tmp 2]
270        lappend rv [r hget smallhash tmp]
271        lappend rv [r hincrby bighash tmp 2]
272        lappend rv [r hget bighash tmp]
273    } {2 2 2 2}
274
275    test {HINCRBY against hash key created by hincrby itself} {
276        set rv {}
277        lappend rv [r hincrby smallhash tmp 3]
278        lappend rv [r hget smallhash tmp]
279        lappend rv [r hincrby bighash tmp 3]
280        lappend rv [r hget bighash tmp]
281    } {5 5 5 5}
282
283    test {HINCRBY against hash key originally set with HSET} {
284        r hset smallhash tmp 100
285        r hset bighash tmp 100
286        list [r hincrby smallhash tmp 2] [r hincrby bighash tmp 2]
287    } {102 102}
288
289    test {HINCRBY over 32bit value} {
290        r hset smallhash tmp 17179869184
291        r hset bighash tmp 17179869184
292        list [r hincrby smallhash tmp 1] [r hincrby bighash tmp 1]
293    } {17179869185 17179869185}
294
295    test {HINCRBY over 32bit value with over 32bit increment} {
296        r hset smallhash tmp 17179869184
297        r hset bighash tmp 17179869184
298        list [r hincrby smallhash tmp 17179869184] [r hincrby bighash tmp 17179869184]
299    } {34359738368 34359738368}
300
301    test {HINCRBY fails against hash value with spaces (left)} {
302        r hset smallhash str " 11"
303        r hset bighash str " 11"
304        catch {r hincrby smallhash str 1} smallerr
305        catch {r hincrby smallhash str 1} bigerr
306        set rv {}
307        lappend rv [string match "ERR*not an integer*" $smallerr]
308        lappend rv [string match "ERR*not an integer*" $bigerr]
309    } {1 1}
310
311    test {HINCRBY fails against hash value with spaces (right)} {
312        r hset smallhash str "11 "
313        r hset bighash str "11 "
314        catch {r hincrby smallhash str 1} smallerr
315        catch {r hincrby smallhash str 1} bigerr
316        set rv {}
317        lappend rv [string match "ERR*not an integer*" $smallerr]
318        lappend rv [string match "ERR*not an integer*" $bigerr]
319    } {1 1}
320
321    test {HINCRBY can detect overflows} {
322        set e {}
323        r hset hash n -9223372036854775484
324        assert {[r hincrby hash n -1] == -9223372036854775485}
325        catch {r hincrby hash n -10000} e
326        set e
327    } {*overflow*}
328
329    test {HINCRBYFLOAT against non existing database key} {
330        r del htest
331        list [r hincrbyfloat htest foo 2.5]
332    } {2.5}
333
334    test {HINCRBYFLOAT against non existing hash key} {
335        set rv {}
336        r hdel smallhash tmp
337        r hdel bighash tmp
338        lappend rv [roundFloat [r hincrbyfloat smallhash tmp 2.5]]
339        lappend rv [roundFloat [r hget smallhash tmp]]
340        lappend rv [roundFloat [r hincrbyfloat bighash tmp 2.5]]
341        lappend rv [roundFloat [r hget bighash tmp]]
342    } {2.5 2.5 2.5 2.5}
343
344    test {HINCRBYFLOAT against hash key created by hincrby itself} {
345        set rv {}
346        lappend rv [roundFloat [r hincrbyfloat smallhash tmp 3.5]]
347        lappend rv [roundFloat [r hget smallhash tmp]]
348        lappend rv [roundFloat [r hincrbyfloat bighash tmp 3.5]]
349        lappend rv [roundFloat [r hget bighash tmp]]
350    } {6 6 6 6}
351
352    test {HINCRBYFLOAT against hash key originally set with HSET} {
353        r hset smallhash tmp 100
354        r hset bighash tmp 100
355        list [roundFloat [r hincrbyfloat smallhash tmp 2.5]] \
356             [roundFloat [r hincrbyfloat bighash tmp 2.5]]
357    } {102.5 102.5}
358
359    test {HINCRBYFLOAT over 32bit value} {
360        r hset smallhash tmp 17179869184
361        r hset bighash tmp 17179869184
362        list [r hincrbyfloat smallhash tmp 1] \
363             [r hincrbyfloat bighash tmp 1]
364    } {17179869185 17179869185}
365
366    test {HINCRBYFLOAT over 32bit value with over 32bit increment} {
367        r hset smallhash tmp 17179869184
368        r hset bighash tmp 17179869184
369        list [r hincrbyfloat smallhash tmp 17179869184] \
370             [r hincrbyfloat bighash tmp 17179869184]
371    } {34359738368 34359738368}
372
373    test {HINCRBYFLOAT fails against hash value with spaces (left)} {
374        r hset smallhash str " 11"
375        r hset bighash str " 11"
376        catch {r hincrbyfloat smallhash str 1} smallerr
377        catch {r hincrbyfloat smallhash str 1} bigerr
378        set rv {}
379        lappend rv [string match "ERR*not*float*" $smallerr]
380        lappend rv [string match "ERR*not*float*" $bigerr]
381    } {1 1}
382
383    test {HINCRBYFLOAT fails against hash value with spaces (right)} {
384        r hset smallhash str "11 "
385        r hset bighash str "11 "
386        catch {r hincrbyfloat smallhash str 1} smallerr
387        catch {r hincrbyfloat smallhash str 1} bigerr
388        set rv {}
389        lappend rv [string match "ERR*not*float*" $smallerr]
390        lappend rv [string match "ERR*not*float*" $bigerr]
391    } {1 1}
392
393    test {HSTRLEN against the small hash} {
394        set err {}
395        foreach k [array names smallhash *] {
396            if {[string length $smallhash($k)] ne [r hstrlen smallhash $k]} {
397                set err "[string length $smallhash($k)] != [r hstrlen smallhash $k]"
398                break
399            }
400        }
401        set _ $err
402    } {}
403
404    test {HSTRLEN against the big hash} {
405        set err {}
406        foreach k [array names bighash *] {
407            if {[string length $bighash($k)] ne [r hstrlen bighash $k]} {
408                set err "[string length $bighash($k)] != [r hstrlen bighash $k]"
409                puts "HSTRLEN and logical length mismatch:"
410                puts "key: $k"
411                puts "Logical content: $bighash($k)"
412                puts "Server  content: [r hget bighash $k]"
413            }
414        }
415        set _ $err
416    } {}
417
418    test {HSTRLEN against non existing field} {
419        set rv {}
420        lappend rv [r hstrlen smallhash __123123123__]
421        lappend rv [r hstrlen bighash __123123123__]
422        set _ $rv
423    } {0 0}
424
425    test {HSTRLEN corner cases} {
426        set vals {
427            -9223372036854775808 9223372036854775807 9223372036854775808
428            {} 0 -1 x
429        }
430        foreach v $vals {
431            r hmset smallhash field $v
432            r hmset bighash field $v
433            set len1 [string length $v]
434            set len2 [r hstrlen smallhash field]
435            set len3 [r hstrlen bighash field]
436            assert {$len1 == $len2}
437            assert {$len2 == $len3}
438        }
439    }
440
441    test {Hash ziplist regression test for large keys} {
442        r hset hash kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk a
443        r hset hash kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk b
444        r hget hash kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
445    } {b}
446
447    foreach size {10 512} {
448        test "Hash fuzzing #1 - $size fields" {
449            for {set times 0} {$times < 10} {incr times} {
450                catch {unset hash}
451                array set hash {}
452                r del hash
453
454                # Create
455                for {set j 0} {$j < $size} {incr j} {
456                    set field [randomValue]
457                    set value [randomValue]
458                    r hset hash $field $value
459                    set hash($field) $value
460                }
461
462                # Verify
463                foreach {k v} [array get hash] {
464                    assert_equal $v [r hget hash $k]
465                }
466                assert_equal [array size hash] [r hlen hash]
467            }
468        }
469
470        test "Hash fuzzing #2 - $size fields" {
471            for {set times 0} {$times < 10} {incr times} {
472                catch {unset hash}
473                array set hash {}
474                r del hash
475
476                # Create
477                for {set j 0} {$j < $size} {incr j} {
478                    randpath {
479                        set field [randomValue]
480                        set value [randomValue]
481                        r hset hash $field $value
482                        set hash($field) $value
483                    } {
484                        set field [randomSignedInt 512]
485                        set value [randomSignedInt 512]
486                        r hset hash $field $value
487                        set hash($field) $value
488                    } {
489                        randpath {
490                            set field [randomValue]
491                        } {
492                            set field [randomSignedInt 512]
493                        }
494                        r hdel hash $field
495                        unset -nocomplain hash($field)
496                    }
497                }
498
499                # Verify
500                foreach {k v} [array get hash] {
501                    assert_equal $v [r hget hash $k]
502                }
503                assert_equal [array size hash] [r hlen hash]
504            }
505        }
506    }
507
508    test {Stress test the hash ziplist -> hashtable encoding conversion} {
509        r config set hash-max-ziplist-entries 32
510        for {set j 0} {$j < 100} {incr j} {
511            r del myhash
512            for {set i 0} {$i < 64} {incr i} {
513                r hset myhash [randomValue] [randomValue]
514            }
515            assert {[r object encoding myhash] eq {hashtable}}
516        }
517    }
518}
519