Align SUNIONCARD LIMIT 0 semantics with SINTERCARD convention

SINTERCARD treats LIMIT 0 as "no limit" while SUNIONCARD was returning
0 immediately. Align the behavior so LIMIT 0 means "no limit" for both
commands. Remove the have_limit flag and early return, and change the
APPROX path checks from have_limit to limit > 0.
This commit is contained in:
Hristo Staykov 2026-03-16 19:15:45 +02:00
parent 19cadde9ee
commit e2cc2b3e09
2 changed files with 10 additions and 16 deletions

View file

@ -1859,8 +1859,7 @@ void sunionCommand(client *c) {
void sunioncardCommand(client *c) {
long j;
long numkeys = 0;
long limit = 0;
int have_limit = 0;
long limit = 0; /* 0 means no limit. */
int approx = 0;
if (getRangeLongFromObjectOrReply(c, c->argv[1], 1, LONG_MAX,
@ -1880,7 +1879,6 @@ void sunioncardCommand(client *c) {
if (getPositiveLongFromObjectOrReply(c, c->argv[j], &limit,
"LIMIT can't be negative") != C_OK)
return;
have_limit = 1;
} else if (!strcasecmp(opt, "APPROX")) {
approx = 1;
} else {
@ -1889,11 +1887,6 @@ void sunioncardCommand(client *c) {
}
}
if (have_limit && limit == 0) {
addReplyLongLong(c, 0);
return;
}
if (approx) {
/* HLL-based approximate cardinality: use a temporary HLL object
* with the standard sparsedense encoding (same as PFADD). */
@ -1938,7 +1931,7 @@ void sunioncardCommand(client *c) {
}
elements_processed++;
if (have_limit &&
if (limit > 0 &&
(elements_processed % HLL_APPROX_CHECK_INTERVAL == 0)) {
uint64_t est = hllCount(hllobj->ptr, NULL);
if (est >= (uint64_t)limit) {
@ -1951,7 +1944,7 @@ void sunioncardCommand(client *c) {
}
uint64_t cardinality = hllCount(hllobj->ptr, NULL);
if (have_limit && cardinality > (uint64_t)limit)
if (limit > 0 && cardinality > (uint64_t)limit)
cardinality = (uint64_t)limit;
if (server.memory_tracking_enabled) {

View file

@ -306,11 +306,11 @@ foreach type {single multiple single_multiple} {
assert_equal $exact $approx
}
test "SUNIONCARD LIMIT 0 returns 0 immediately" {
test "SUNIONCARD LIMIT 0 means no limit" {
r del set1{t} set2{t}
r sadd set1{t} a b c
r sadd set2{t} c d e
assert_equal 0 [r sunioncard 2 set1{t} set2{t} LIMIT 0]
assert_equal 5 [r sunioncard 2 set1{t} set2{t} LIMIT 0]
}
test "SUNIONCARD with both APPROX and LIMIT" {
@ -318,7 +318,7 @@ foreach type {single multiple single_multiple} {
r sadd set1{t} a b c
r sadd set2{t} c d e
assert_equal 3 [r sunioncard 2 set1{t} set2{t} APPROX LIMIT 3]
assert_equal 0 [r sunioncard 2 set1{t} set2{t} APPROX LIMIT 0]
assert_equal 5 [r sunioncard 2 set1{t} set2{t} APPROX LIMIT 0]
}
test "SUNIONCARD APPROX with large sets is within HLL error margin" {
@ -421,7 +421,7 @@ foreach type {single multiple single_multiple} {
test "SUNIONCARD with two sets - $type" {
set expected [llength [lsort -uniq "[r smembers set1{t}] [r smembers set2{t}]"]]
assert_equal $expected [r sunioncard 2 set1{t} set2{t}]
assert_equal 0 [r sunioncard 2 set1{t} set2{t} LIMIT 0]
assert_equal $expected [r sunioncard 2 set1{t} set2{t} LIMIT 0]
assert_equal 3 [r sunioncard 2 set1{t} set2{t} LIMIT 3]
assert_equal $expected [r sunioncard 2 set1{t} set2{t} LIMIT 10000]
}
@ -429,7 +429,7 @@ foreach type {single multiple single_multiple} {
test "SUNIONCARD against three sets - $type" {
set expected [llength [lsort -uniq "[r smembers set1{t}] [r smembers set2{t}] [r smembers set3{t}]"]]
assert_equal $expected [r sunioncard 3 set1{t} set2{t} set3{t}]
assert_equal 0 [r sunioncard 3 set1{t} set2{t} set3{t} LIMIT 0]
assert_equal $expected [r sunioncard 3 set1{t} set2{t} set3{t} LIMIT 0]
assert_equal 2 [r sunioncard 3 set1{t} set2{t} set3{t} LIMIT 2]
assert_equal $expected [r sunioncard 3 set1{t} set2{t} set3{t} LIMIT 10000]
}
@ -447,7 +447,8 @@ foreach type {single multiple single_multiple} {
}
test "SUNIONCARD APPROX with LIMIT - $type" {
assert_equal 0 [r sunioncard 2 set1{t} set2{t} APPROX LIMIT 0]
set approx_no_limit [r sunioncard 2 set1{t} set2{t} APPROX]
assert_equal $approx_no_limit [r sunioncard 2 set1{t} set2{t} APPROX LIMIT 0]
assert_equal 10 [r sunioncard 2 set1{t} set2{t} APPROX LIMIT 10]
}