823 lines
29 KiB
Tcl
823 lines
29 KiB
Tcl
start_server {tags {"hash"}} {
|
|
test {HSET/HLEN - Small hash creation} {
|
|
array set smallhash {}
|
|
for {set i 0} {$i < 8} {incr i} {
|
|
set key __avoid_collisions__[randstring 0 8 alpha]
|
|
set val __avoid_collisions__[randstring 0 8 alpha]
|
|
if {[info exists smallhash($key)]} {
|
|
incr i -1
|
|
continue
|
|
}
|
|
r hset smallhash $key $val
|
|
set smallhash($key) $val
|
|
}
|
|
list [r hlen smallhash]
|
|
} {8}
|
|
|
|
test {Is the small hash encoded with a listpack?} {
|
|
assert_encoding listpack smallhash
|
|
}
|
|
|
|
proc create_hash {key entries} {
|
|
r del $key
|
|
foreach entry $entries {
|
|
r hset $key [lindex $entry 0] [lindex $entry 1]
|
|
}
|
|
}
|
|
|
|
proc get_keys {l} {
|
|
set res {}
|
|
foreach entry $l {
|
|
set key [lindex $entry 0]
|
|
lappend res $key
|
|
}
|
|
return $res
|
|
}
|
|
|
|
foreach {type contents} "listpack {{a 1} {b 2} {c 3}} hashtable {{a 1} {b 2} {[randstring 70 90 alpha] 3}}" {
|
|
set original_max_value [lindex [r config get hash-max-ziplist-value] 1]
|
|
r config set hash-max-ziplist-value 10
|
|
create_hash myhash $contents
|
|
assert_encoding $type myhash
|
|
|
|
# coverage for objectComputeSize
|
|
assert_morethan [memory_usage myhash] 0
|
|
|
|
test "HRANDFIELD - $type" {
|
|
unset -nocomplain myhash
|
|
array set myhash {}
|
|
for {set i 0} {$i < 100} {incr i} {
|
|
set key [r hrandfield myhash]
|
|
set myhash($key) 1
|
|
}
|
|
assert_equal [lsort [get_keys $contents]] [lsort [array names myhash]]
|
|
}
|
|
r config set hash-max-ziplist-value $original_max_value
|
|
}
|
|
|
|
test "HRANDFIELD with RESP3" {
|
|
r hello 3
|
|
set res [r hrandfield myhash 3 withvalues]
|
|
assert_equal [llength $res] 3
|
|
assert_equal [llength [lindex $res 1]] 2
|
|
|
|
set res [r hrandfield myhash 3]
|
|
assert_equal [llength $res] 3
|
|
assert_equal [llength [lindex $res 1]] 1
|
|
r hello 2
|
|
}
|
|
|
|
test "HRANDFIELD count of 0 is handled correctly" {
|
|
r hrandfield myhash 0
|
|
} {}
|
|
|
|
test "HRANDFIELD with <count> against non existing key" {
|
|
r hrandfield nonexisting_key 100
|
|
} {}
|
|
|
|
# Make sure we can distinguish between an empty array and a null response
|
|
r readraw 1
|
|
|
|
test "HRANDFIELD count of 0 is handled correctly - emptyarray" {
|
|
r hrandfield myhash 0
|
|
} {*0}
|
|
|
|
test "HRANDFIELD with <count> against non existing key - emptyarray" {
|
|
r hrandfield nonexisting_key 100
|
|
} {*0}
|
|
|
|
r readraw 0
|
|
|
|
foreach {type contents} "
|
|
hashtable {{a 1} {b 2} {c 3} {d 4} {e 5} {6 f} {7 g} {8 h} {9 i} {[randstring 70 90 alpha] 10}}
|
|
listpack {{a 1} {b 2} {c 3} {d 4} {e 5} {6 f} {7 g} {8 h} {9 i} {10 j}} " {
|
|
test "HRANDFIELD with <count> - $type" {
|
|
set original_max_value [lindex [r config get hash-max-ziplist-value] 1]
|
|
r config set hash-max-ziplist-value 10
|
|
create_hash myhash $contents
|
|
assert_encoding $type myhash
|
|
|
|
# create a dict for easy lookup
|
|
set mydict [dict create {*}[r hgetall myhash]]
|
|
|
|
# We'll stress different parts of the code, see the implementation
|
|
# of HRANDFIELD for more information, but basically there are
|
|
# four different code paths.
|
|
|
|
# PATH 1: Use negative count.
|
|
|
|
# 1) Check that it returns repeated elements with and without values.
|
|
set res [r hrandfield myhash -20]
|
|
assert_equal [llength $res] 20
|
|
set res [r hrandfield myhash -1001]
|
|
assert_equal [llength $res] 1001
|
|
# again with WITHVALUES
|
|
set res [r hrandfield myhash -20 withvalues]
|
|
assert_equal [llength $res] 40
|
|
set res [r hrandfield myhash -1001 withvalues]
|
|
assert_equal [llength $res] 2002
|
|
|
|
# Test random uniform distribution
|
|
# df = 9, 40 means 0.00001 probability
|
|
set res [r hrandfield myhash -1000]
|
|
assert_lessthan [chi_square_value $res] 40
|
|
|
|
# 2) Check that all the elements actually belong to the original hash.
|
|
foreach {key val} $res {
|
|
assert {[dict exists $mydict $key]}
|
|
}
|
|
|
|
# 3) Check that eventually all the elements are returned.
|
|
# Use both WITHVALUES and without
|
|
unset -nocomplain auxset
|
|
set iterations 1000
|
|
while {$iterations != 0} {
|
|
incr iterations -1
|
|
if {[expr {$iterations % 2}] == 0} {
|
|
set res [r hrandfield myhash -3 withvalues]
|
|
foreach {key val} $res {
|
|
dict append auxset $key $val
|
|
}
|
|
} else {
|
|
set res [r hrandfield myhash -3]
|
|
foreach key $res {
|
|
dict append auxset $key $val
|
|
}
|
|
}
|
|
if {[lsort [dict keys $mydict]] eq
|
|
[lsort [dict keys $auxset]]} {
|
|
break;
|
|
}
|
|
}
|
|
assert {$iterations != 0}
|
|
|
|
# PATH 2: positive count (unique behavior) with requested size
|
|
# equal or greater than set size.
|
|
foreach size {10 20} {
|
|
set res [r hrandfield myhash $size]
|
|
assert_equal [llength $res] 10
|
|
assert_equal [lsort $res] [lsort [dict keys $mydict]]
|
|
|
|
# again with WITHVALUES
|
|
set res [r hrandfield myhash $size withvalues]
|
|
assert_equal [llength $res] 20
|
|
assert_equal [lsort $res] [lsort $mydict]
|
|
}
|
|
|
|
# PATH 3: Ask almost as elements as there are in the set.
|
|
# In this case the implementation will duplicate the original
|
|
# set and will remove random elements up to the requested size.
|
|
#
|
|
# PATH 4: Ask a number of elements definitely smaller than
|
|
# the set size.
|
|
#
|
|
# We can test both the code paths just changing the size but
|
|
# using the same code.
|
|
foreach size {8 2} {
|
|
set res [r hrandfield myhash $size]
|
|
assert_equal [llength $res] $size
|
|
# again with WITHVALUES
|
|
set res [r hrandfield myhash $size withvalues]
|
|
assert_equal [llength $res] [expr {$size * 2}]
|
|
|
|
# 1) Check that all the elements actually belong to the
|
|
# original set.
|
|
foreach ele [dict keys $res] {
|
|
assert {[dict exists $mydict $ele]}
|
|
}
|
|
|
|
# 2) Check that eventually all the elements are returned.
|
|
# Use both WITHVALUES and without
|
|
unset -nocomplain auxset
|
|
unset -nocomplain allkey
|
|
set iterations [expr {1000 / $size}]
|
|
set all_ele_return false
|
|
while {$iterations != 0} {
|
|
incr iterations -1
|
|
if {[expr {$iterations % 2}] == 0} {
|
|
set res [r hrandfield myhash $size withvalues]
|
|
foreach {key value} $res {
|
|
dict append auxset $key $value
|
|
lappend allkey $key
|
|
}
|
|
} else {
|
|
set res [r hrandfield myhash $size]
|
|
foreach key $res {
|
|
dict append auxset $key
|
|
lappend allkey $key
|
|
}
|
|
}
|
|
if {[lsort [dict keys $mydict]] eq
|
|
[lsort [dict keys $auxset]]} {
|
|
set all_ele_return true
|
|
}
|
|
}
|
|
assert_equal $all_ele_return true
|
|
# df = 9, 40 means 0.00001 probability
|
|
assert_lessthan [chi_square_value $allkey] 40
|
|
}
|
|
}
|
|
r config set hash-max-ziplist-value $original_max_value
|
|
}
|
|
|
|
|
|
test {HSET/HLEN - Big hash creation} {
|
|
array set bighash {}
|
|
for {set i 0} {$i < 1024} {incr i} {
|
|
set key __avoid_collisions__[randstring 0 8 alpha]
|
|
set val __avoid_collisions__[randstring 0 8 alpha]
|
|
if {[info exists bighash($key)]} {
|
|
incr i -1
|
|
continue
|
|
}
|
|
r hset bighash $key $val
|
|
set bighash($key) $val
|
|
}
|
|
list [r hlen bighash]
|
|
} {1024}
|
|
|
|
test {Is the big hash encoded with an hash table?} {
|
|
assert_encoding hashtable bighash
|
|
}
|
|
|
|
test {HGET against the small hash} {
|
|
set err {}
|
|
foreach k [array names smallhash *] {
|
|
if {$smallhash($k) ne [r hget smallhash $k]} {
|
|
set err "$smallhash($k) != [r hget smallhash $k]"
|
|
break
|
|
}
|
|
}
|
|
set _ $err
|
|
} {}
|
|
|
|
test {HGET against the big hash} {
|
|
set err {}
|
|
foreach k [array names bighash *] {
|
|
if {$bighash($k) ne [r hget bighash $k]} {
|
|
set err "$bighash($k) != [r hget bighash $k]"
|
|
break
|
|
}
|
|
}
|
|
set _ $err
|
|
} {}
|
|
|
|
test {HGET against non existing key} {
|
|
set rv {}
|
|
lappend rv [r hget smallhash __123123123__]
|
|
lappend rv [r hget bighash __123123123__]
|
|
set _ $rv
|
|
} {{} {}}
|
|
|
|
test {HSET in update and insert mode} {
|
|
set rv {}
|
|
set k [lindex [array names smallhash *] 0]
|
|
lappend rv [r hset smallhash $k newval1]
|
|
set smallhash($k) newval1
|
|
lappend rv [r hget smallhash $k]
|
|
lappend rv [r hset smallhash __foobar123__ newval]
|
|
set k [lindex [array names bighash *] 0]
|
|
lappend rv [r hset bighash $k newval2]
|
|
set bighash($k) newval2
|
|
lappend rv [r hget bighash $k]
|
|
lappend rv [r hset bighash __foobar123__ newval]
|
|
lappend rv [r hdel smallhash __foobar123__]
|
|
lappend rv [r hdel bighash __foobar123__]
|
|
set _ $rv
|
|
} {0 newval1 1 0 newval2 1 1 1}
|
|
|
|
test {HSETNX target key missing - small hash} {
|
|
r hsetnx smallhash __123123123__ foo
|
|
r hget smallhash __123123123__
|
|
} {foo}
|
|
|
|
test {HSETNX target key exists - small hash} {
|
|
r hsetnx smallhash __123123123__ bar
|
|
set result [r hget smallhash __123123123__]
|
|
r hdel smallhash __123123123__
|
|
set _ $result
|
|
} {foo}
|
|
|
|
test {HSETNX target key missing - big hash} {
|
|
r hsetnx bighash __123123123__ foo
|
|
r hget bighash __123123123__
|
|
} {foo}
|
|
|
|
test {HSETNX target key exists - big hash} {
|
|
r hsetnx bighash __123123123__ bar
|
|
set result [r hget bighash __123123123__]
|
|
r hdel bighash __123123123__
|
|
set _ $result
|
|
} {foo}
|
|
|
|
test {HSET/HMSET wrong number of args} {
|
|
assert_error {*wrong number of arguments for 'hset' command} {r hset smallhash key1 val1 key2}
|
|
assert_error {*wrong number of arguments for 'hmset' command} {r hmset smallhash key1 val1 key2}
|
|
}
|
|
|
|
test {HMSET - small hash} {
|
|
set args {}
|
|
foreach {k v} [array get smallhash] {
|
|
set newval [randstring 0 8 alpha]
|
|
set smallhash($k) $newval
|
|
lappend args $k $newval
|
|
}
|
|
r hmset smallhash {*}$args
|
|
} {OK}
|
|
|
|
test {HMSET - big hash} {
|
|
set args {}
|
|
foreach {k v} [array get bighash] {
|
|
set newval [randstring 0 8 alpha]
|
|
set bighash($k) $newval
|
|
lappend args $k $newval
|
|
}
|
|
r hmset bighash {*}$args
|
|
} {OK}
|
|
|
|
test {HMGET against non existing key and fields} {
|
|
set rv {}
|
|
lappend rv [r hmget doesntexist __123123123__ __456456456__]
|
|
lappend rv [r hmget smallhash __123123123__ __456456456__]
|
|
lappend rv [r hmget bighash __123123123__ __456456456__]
|
|
set _ $rv
|
|
} {{{} {}} {{} {}} {{} {}}}
|
|
|
|
test {HMGET against wrong type} {
|
|
r set wrongtype somevalue
|
|
assert_error "*wrong*" {r hmget wrongtype field1 field2}
|
|
}
|
|
|
|
test {HMGET - small hash} {
|
|
set keys {}
|
|
set vals {}
|
|
foreach {k v} [array get smallhash] {
|
|
lappend keys $k
|
|
lappend vals $v
|
|
}
|
|
set err {}
|
|
set result [r hmget smallhash {*}$keys]
|
|
if {$vals ne $result} {
|
|
set err "$vals != $result"
|
|
break
|
|
}
|
|
set _ $err
|
|
} {}
|
|
|
|
test {HMGET - big hash} {
|
|
set keys {}
|
|
set vals {}
|
|
foreach {k v} [array get bighash] {
|
|
lappend keys $k
|
|
lappend vals $v
|
|
}
|
|
set err {}
|
|
set result [r hmget bighash {*}$keys]
|
|
if {$vals ne $result} {
|
|
set err "$vals != $result"
|
|
break
|
|
}
|
|
set _ $err
|
|
} {}
|
|
|
|
test {HKEYS - small hash} {
|
|
lsort [r hkeys smallhash]
|
|
} [lsort [array names smallhash *]]
|
|
|
|
test {HKEYS - big hash} {
|
|
lsort [r hkeys bighash]
|
|
} [lsort [array names bighash *]]
|
|
|
|
test {HVALS - small hash} {
|
|
set vals {}
|
|
foreach {k v} [array get smallhash] {
|
|
lappend vals $v
|
|
}
|
|
set _ [lsort $vals]
|
|
} [lsort [r hvals smallhash]]
|
|
|
|
test {HVALS - big hash} {
|
|
set vals {}
|
|
foreach {k v} [array get bighash] {
|
|
lappend vals $v
|
|
}
|
|
set _ [lsort $vals]
|
|
} [lsort [r hvals bighash]]
|
|
|
|
test {HGETALL - small hash} {
|
|
lsort [r hgetall smallhash]
|
|
} [lsort [array get smallhash]]
|
|
|
|
test {HGETALL - big hash} {
|
|
lsort [r hgetall bighash]
|
|
} [lsort [array get bighash]]
|
|
|
|
test {HDEL and return value} {
|
|
set rv {}
|
|
lappend rv [r hdel smallhash nokey]
|
|
lappend rv [r hdel bighash nokey]
|
|
set k [lindex [array names smallhash *] 0]
|
|
lappend rv [r hdel smallhash $k]
|
|
lappend rv [r hdel smallhash $k]
|
|
lappend rv [r hget smallhash $k]
|
|
unset smallhash($k)
|
|
set k [lindex [array names bighash *] 0]
|
|
lappend rv [r hdel bighash $k]
|
|
lappend rv [r hdel bighash $k]
|
|
lappend rv [r hget bighash $k]
|
|
unset bighash($k)
|
|
set _ $rv
|
|
} {0 0 1 0 {} 1 0 {}}
|
|
|
|
test {HDEL - more than a single value} {
|
|
set rv {}
|
|
r del myhash
|
|
r hmset myhash a 1 b 2 c 3
|
|
assert_equal 0 [r hdel myhash x y]
|
|
assert_equal 2 [r hdel myhash a c f]
|
|
r hgetall myhash
|
|
} {b 2}
|
|
|
|
test {HDEL - hash becomes empty before deleting all specified fields} {
|
|
r del myhash
|
|
r hmset myhash a 1 b 2 c 3
|
|
assert_equal 3 [r hdel myhash a b c d e]
|
|
assert_equal 0 [r exists myhash]
|
|
}
|
|
|
|
test {HEXISTS} {
|
|
set rv {}
|
|
set k [lindex [array names smallhash *] 0]
|
|
lappend rv [r hexists smallhash $k]
|
|
lappend rv [r hexists smallhash nokey]
|
|
set k [lindex [array names bighash *] 0]
|
|
lappend rv [r hexists bighash $k]
|
|
lappend rv [r hexists bighash nokey]
|
|
} {1 0 1 0}
|
|
|
|
test {Is a ziplist encoded Hash promoted on big payload?} {
|
|
r hset smallhash foo [string repeat a 1024]
|
|
r debug object smallhash
|
|
} {*hashtable*} {needs:debug}
|
|
|
|
test {HINCRBY against non existing database key} {
|
|
r del htest
|
|
list [r hincrby htest foo 2]
|
|
} {2}
|
|
|
|
test {HINCRBY against non existing hash key} {
|
|
set rv {}
|
|
r hdel smallhash tmp
|
|
r hdel bighash tmp
|
|
lappend rv [r hincrby smallhash tmp 2]
|
|
lappend rv [r hget smallhash tmp]
|
|
lappend rv [r hincrby bighash tmp 2]
|
|
lappend rv [r hget bighash tmp]
|
|
} {2 2 2 2}
|
|
|
|
test {HINCRBY against hash key created by hincrby itself} {
|
|
set rv {}
|
|
lappend rv [r hincrby smallhash tmp 3]
|
|
lappend rv [r hget smallhash tmp]
|
|
lappend rv [r hincrby bighash tmp 3]
|
|
lappend rv [r hget bighash tmp]
|
|
} {5 5 5 5}
|
|
|
|
test {HINCRBY against hash key originally set with HSET} {
|
|
r hset smallhash tmp 100
|
|
r hset bighash tmp 100
|
|
list [r hincrby smallhash tmp 2] [r hincrby bighash tmp 2]
|
|
} {102 102}
|
|
|
|
test {HINCRBY over 32bit value} {
|
|
r hset smallhash tmp 17179869184
|
|
r hset bighash tmp 17179869184
|
|
list [r hincrby smallhash tmp 1] [r hincrby bighash tmp 1]
|
|
} {17179869185 17179869185}
|
|
|
|
test {HINCRBY over 32bit value with over 32bit increment} {
|
|
r hset smallhash tmp 17179869184
|
|
r hset bighash tmp 17179869184
|
|
list [r hincrby smallhash tmp 17179869184] [r hincrby bighash tmp 17179869184]
|
|
} {34359738368 34359738368}
|
|
|
|
test {HINCRBY fails against hash value with spaces (left)} {
|
|
r hset smallhash str " 11"
|
|
r hset bighash str " 11"
|
|
catch {r hincrby smallhash str 1} smallerr
|
|
catch {r hincrby bighash str 1} bigerr
|
|
set rv {}
|
|
lappend rv [string match "ERR*not an integer*" $smallerr]
|
|
lappend rv [string match "ERR*not an integer*" $bigerr]
|
|
} {1 1}
|
|
|
|
test {HINCRBY fails against hash value with spaces (right)} {
|
|
r hset smallhash str "11 "
|
|
r hset bighash str "11 "
|
|
catch {r hincrby smallhash str 1} smallerr
|
|
catch {r hincrby bighash str 1} bigerr
|
|
set rv {}
|
|
lappend rv [string match "ERR*not an integer*" $smallerr]
|
|
lappend rv [string match "ERR*not an integer*" $bigerr]
|
|
} {1 1}
|
|
|
|
test {HINCRBY can detect overflows} {
|
|
set e {}
|
|
r hset hash n -9223372036854775484
|
|
assert {[r hincrby hash n -1] == -9223372036854775485}
|
|
catch {r hincrby hash n -10000} e
|
|
set e
|
|
} {*overflow*}
|
|
|
|
test {HINCRBYFLOAT against non existing database key} {
|
|
r del htest
|
|
list [r hincrbyfloat htest foo 2.5]
|
|
} {2.5}
|
|
|
|
test {HINCRBYFLOAT against non existing hash key} {
|
|
set rv {}
|
|
r hdel smallhash tmp
|
|
r hdel bighash tmp
|
|
lappend rv [roundFloat [r hincrbyfloat smallhash tmp 2.5]]
|
|
lappend rv [roundFloat [r hget smallhash tmp]]
|
|
lappend rv [roundFloat [r hincrbyfloat bighash tmp 2.5]]
|
|
lappend rv [roundFloat [r hget bighash tmp]]
|
|
} {2.5 2.5 2.5 2.5}
|
|
|
|
test {HINCRBYFLOAT against hash key created by hincrby itself} {
|
|
set rv {}
|
|
lappend rv [roundFloat [r hincrbyfloat smallhash tmp 3.5]]
|
|
lappend rv [roundFloat [r hget smallhash tmp]]
|
|
lappend rv [roundFloat [r hincrbyfloat bighash tmp 3.5]]
|
|
lappend rv [roundFloat [r hget bighash tmp]]
|
|
} {6 6 6 6}
|
|
|
|
test {HINCRBYFLOAT against hash key originally set with HSET} {
|
|
r hset smallhash tmp 100
|
|
r hset bighash tmp 100
|
|
list [roundFloat [r hincrbyfloat smallhash tmp 2.5]] \
|
|
[roundFloat [r hincrbyfloat bighash tmp 2.5]]
|
|
} {102.5 102.5}
|
|
|
|
test {HINCRBYFLOAT over 32bit value} {
|
|
r hset smallhash tmp 17179869184
|
|
r hset bighash tmp 17179869184
|
|
list [r hincrbyfloat smallhash tmp 1] \
|
|
[r hincrbyfloat bighash tmp 1]
|
|
} {17179869185 17179869185}
|
|
|
|
test {HINCRBYFLOAT over 32bit value with over 32bit increment} {
|
|
r hset smallhash tmp 17179869184
|
|
r hset bighash tmp 17179869184
|
|
list [r hincrbyfloat smallhash tmp 17179869184] \
|
|
[r hincrbyfloat bighash tmp 17179869184]
|
|
} {34359738368 34359738368}
|
|
|
|
test {HINCRBYFLOAT fails against hash value with spaces (left)} {
|
|
r hset smallhash str " 11"
|
|
r hset bighash str " 11"
|
|
catch {r hincrbyfloat smallhash str 1} smallerr
|
|
catch {r hincrbyfloat bighash str 1} bigerr
|
|
set rv {}
|
|
lappend rv [string match "ERR*not*float*" $smallerr]
|
|
lappend rv [string match "ERR*not*float*" $bigerr]
|
|
} {1 1}
|
|
|
|
test {HINCRBYFLOAT fails against hash value with spaces (right)} {
|
|
r hset smallhash str "11 "
|
|
r hset bighash str "11 "
|
|
catch {r hincrbyfloat smallhash str 1} smallerr
|
|
catch {r hincrbyfloat bighash str 1} bigerr
|
|
set rv {}
|
|
lappend rv [string match "ERR*not*float*" $smallerr]
|
|
lappend rv [string match "ERR*not*float*" $bigerr]
|
|
} {1 1}
|
|
|
|
test {HINCRBYFLOAT fails against hash value that contains a null-terminator in the middle} {
|
|
r hset h f "1\x002"
|
|
catch {r hincrbyfloat h f 1} err
|
|
set rv {}
|
|
lappend rv [string match "ERR*not*float*" $err]
|
|
} {1}
|
|
|
|
test {HSTRLEN against the small hash} {
|
|
set err {}
|
|
foreach k [array names smallhash *] {
|
|
if {[string length $smallhash($k)] ne [r hstrlen smallhash $k]} {
|
|
set err "[string length $smallhash($k)] != [r hstrlen smallhash $k]"
|
|
break
|
|
}
|
|
}
|
|
set _ $err
|
|
} {}
|
|
|
|
test {HSTRLEN against the big hash} {
|
|
set err {}
|
|
foreach k [array names bighash *] {
|
|
if {[string length $bighash($k)] ne [r hstrlen bighash $k]} {
|
|
set err "[string length $bighash($k)] != [r hstrlen bighash $k]"
|
|
puts "HSTRLEN and logical length mismatch:"
|
|
puts "key: $k"
|
|
puts "Logical content: $bighash($k)"
|
|
puts "Server content: [r hget bighash $k]"
|
|
}
|
|
}
|
|
set _ $err
|
|
} {}
|
|
|
|
test {HSTRLEN against non existing field} {
|
|
set rv {}
|
|
lappend rv [r hstrlen smallhash __123123123__]
|
|
lappend rv [r hstrlen bighash __123123123__]
|
|
set _ $rv
|
|
} {0 0}
|
|
|
|
test {HSTRLEN corner cases} {
|
|
set vals {
|
|
-9223372036854775808 9223372036854775807 9223372036854775808
|
|
{} 0 -1 x
|
|
}
|
|
foreach v $vals {
|
|
r hmset smallhash field $v
|
|
r hmset bighash field $v
|
|
set len1 [string length $v]
|
|
set len2 [r hstrlen smallhash field]
|
|
set len3 [r hstrlen bighash field]
|
|
assert {$len1 == $len2}
|
|
assert {$len2 == $len3}
|
|
}
|
|
}
|
|
|
|
test {HINCRBYFLOAT over hash-max-listpack-value encoded with a listpack} {
|
|
set original_max_value [lindex [r config get hash-max-ziplist-value] 1]
|
|
r config set hash-max-listpack-value 8
|
|
|
|
# hash's value exceeds hash-max-listpack-value
|
|
r del smallhash
|
|
r del bighash
|
|
r hset smallhash tmp 0
|
|
r hset bighash tmp 0
|
|
r hincrbyfloat smallhash tmp 0.000005
|
|
r hincrbyfloat bighash tmp 0.0000005
|
|
assert_encoding listpack smallhash
|
|
assert_encoding hashtable bighash
|
|
|
|
# hash's field exceeds hash-max-listpack-value
|
|
r del smallhash
|
|
r del bighash
|
|
r hincrbyfloat smallhash abcdefgh 1
|
|
r hincrbyfloat bighash abcdefghi 1
|
|
assert_encoding listpack smallhash
|
|
assert_encoding hashtable bighash
|
|
|
|
r config set hash-max-listpack-value $original_max_value
|
|
}
|
|
|
|
test {Hash ziplist regression test for large keys} {
|
|
r hset hash kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk a
|
|
r hset hash kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk b
|
|
r hget hash kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
|
|
} {b}
|
|
|
|
foreach size {10 512} {
|
|
test "Hash fuzzing #1 - $size fields" {
|
|
for {set times 0} {$times < 10} {incr times} {
|
|
catch {unset hash}
|
|
array set hash {}
|
|
r del hash
|
|
|
|
# Create
|
|
for {set j 0} {$j < $size} {incr j} {
|
|
set field [randomValue]
|
|
set value [randomValue]
|
|
r hset hash $field $value
|
|
set hash($field) $value
|
|
}
|
|
|
|
# Verify
|
|
foreach {k v} [array get hash] {
|
|
assert_equal $v [r hget hash $k]
|
|
}
|
|
assert_equal [array size hash] [r hlen hash]
|
|
}
|
|
}
|
|
|
|
test "Hash fuzzing #2 - $size fields" {
|
|
for {set times 0} {$times < 10} {incr times} {
|
|
catch {unset hash}
|
|
array set hash {}
|
|
r del hash
|
|
|
|
# Create
|
|
for {set j 0} {$j < $size} {incr j} {
|
|
randpath {
|
|
set field [randomValue]
|
|
set value [randomValue]
|
|
r hset hash $field $value
|
|
set hash($field) $value
|
|
} {
|
|
set field [randomSignedInt 512]
|
|
set value [randomSignedInt 512]
|
|
r hset hash $field $value
|
|
set hash($field) $value
|
|
} {
|
|
randpath {
|
|
set field [randomValue]
|
|
} {
|
|
set field [randomSignedInt 512]
|
|
}
|
|
r hdel hash $field
|
|
unset -nocomplain hash($field)
|
|
}
|
|
}
|
|
|
|
# Verify
|
|
foreach {k v} [array get hash] {
|
|
assert_equal $v [r hget hash $k]
|
|
}
|
|
assert_equal [array size hash] [r hlen hash]
|
|
}
|
|
}
|
|
}
|
|
|
|
test {Stress test the hash ziplist -> hashtable encoding conversion} {
|
|
r config set hash-max-ziplist-entries 32
|
|
for {set j 0} {$j < 100} {incr j} {
|
|
r del myhash
|
|
for {set i 0} {$i < 64} {incr i} {
|
|
r hset myhash [randomValue] [randomValue]
|
|
}
|
|
assert_encoding hashtable myhash
|
|
}
|
|
}
|
|
|
|
# The following test can only be executed if we don't use Valgrind, and if
|
|
# we are using x86_64 architecture, because:
|
|
#
|
|
# 1) Valgrind has floating point limitations, no support for 80 bits math.
|
|
# 2) Other archs may have the same limits.
|
|
#
|
|
# 1.23 cannot be represented correctly with 64 bit doubles, so we skip
|
|
# the test, since we are only testing pretty printing here and is not
|
|
# a bug if the program outputs things like 1.299999...
|
|
if {!$::valgrind && [string match *x86_64* [exec uname -a]]} {
|
|
test {Test HINCRBYFLOAT for correct float representation (issue #2846)} {
|
|
r del myhash
|
|
assert {[r hincrbyfloat myhash float 1.23] eq {1.23}}
|
|
assert {[r hincrbyfloat myhash float 0.77] eq {2}}
|
|
assert {[r hincrbyfloat myhash float -0.1] eq {1.9}}
|
|
}
|
|
}
|
|
|
|
test {Hash ziplist of various encodings} {
|
|
r del k
|
|
config_set hash-max-ziplist-entries 1000000000
|
|
config_set hash-max-ziplist-value 1000000000
|
|
r hset k ZIP_INT_8B 127
|
|
r hset k ZIP_INT_16B 32767
|
|
r hset k ZIP_INT_32B 2147483647
|
|
r hset k ZIP_INT_64B 9223372036854775808
|
|
r hset k ZIP_INT_IMM_MIN 0
|
|
r hset k ZIP_INT_IMM_MAX 12
|
|
r hset k ZIP_STR_06B [string repeat x 31]
|
|
r hset k ZIP_STR_14B [string repeat x 8191]
|
|
r hset k ZIP_STR_32B [string repeat x 65535]
|
|
set k [r hgetall k]
|
|
set dump [r dump k]
|
|
|
|
# will be converted to dict at RESTORE
|
|
config_set hash-max-ziplist-entries 2
|
|
config_set sanitize-dump-payload no mayfail
|
|
r restore kk 0 $dump
|
|
set kk [r hgetall kk]
|
|
|
|
# make sure the values are right
|
|
assert_equal [lsort $k] [lsort $kk]
|
|
assert_equal [dict get $k ZIP_STR_06B] [string repeat x 31]
|
|
set k [dict remove $k ZIP_STR_06B]
|
|
assert_equal [dict get $k ZIP_STR_14B] [string repeat x 8191]
|
|
set k [dict remove $k ZIP_STR_14B]
|
|
assert_equal [dict get $k ZIP_STR_32B] [string repeat x 65535]
|
|
set k [dict remove $k ZIP_STR_32B]
|
|
set _ $k
|
|
} {ZIP_INT_8B 127 ZIP_INT_16B 32767 ZIP_INT_32B 2147483647 ZIP_INT_64B 9223372036854775808 ZIP_INT_IMM_MIN 0 ZIP_INT_IMM_MAX 12}
|
|
|
|
test {Hash ziplist of various encodings - sanitize dump} {
|
|
config_set sanitize-dump-payload yes mayfail
|
|
r restore kk 0 $dump replace
|
|
set k [r hgetall k]
|
|
set kk [r hgetall kk]
|
|
|
|
# make sure the values are right
|
|
assert_equal [lsort $k] [lsort $kk]
|
|
assert_equal [dict get $k ZIP_STR_06B] [string repeat x 31]
|
|
set k [dict remove $k ZIP_STR_06B]
|
|
assert_equal [dict get $k ZIP_STR_14B] [string repeat x 8191]
|
|
set k [dict remove $k ZIP_STR_14B]
|
|
assert_equal [dict get $k ZIP_STR_32B] [string repeat x 65535]
|
|
set k [dict remove $k ZIP_STR_32B]
|
|
set _ $k
|
|
} {ZIP_INT_8B 127 ZIP_INT_16B 32767 ZIP_INT_32B 2147483647 ZIP_INT_64B 9223372036854775808 ZIP_INT_IMM_MIN 0 ZIP_INT_IMM_MAX 12}
|
|
|
|
}
|