mirror of https://github.com/redis/redis.git
				
				
				
			RM_ZsetRem: Delete key if empty (#8453)
Without this fix, RM_ZsetRem can leave empty sorted sets which are
not allowed to exist.
Removing from a sorted set while iterating seems to work (while
inserting causes failed assetions). RM_ZsetRangeEndReached is
modified to return 1 if the key doesn't exist, to terminate
iteration when the last element has been removed.
(cherry picked from commit aea6e71ef8)
			
			
This commit is contained in:
		
							parent
							
								
									cde69883a1
								
							
						
					
					
						commit
						17c3ac89e7
					
				|  | @ -28,4 +28,5 @@ $TCLSH tests/test_helper.tcl \ | |||
| --single unit/moduleapi/keyspace_events \ | ||||
| --single unit/moduleapi/blockedclient \ | ||||
| --single unit/moduleapi/getkeys \ | ||||
| --single unit/moduleapi/zset \ | ||||
| "${@}" | ||||
|  |  | |||
|  | @ -2508,6 +2508,7 @@ int RM_ZsetRem(RedisModuleKey *key, RedisModuleString *ele, int *deleted) { | |||
|     if (key->value && key->value->type != OBJ_ZSET) return REDISMODULE_ERR; | ||||
|     if (key->value != NULL && zsetDel(key->value,ele->ptr)) { | ||||
|         if (deleted) *deleted = 1; | ||||
|         moduleDelKeyIfEmpty(key); | ||||
|     } else { | ||||
|         if (deleted) *deleted = 0; | ||||
|     } | ||||
|  | @ -2552,6 +2553,7 @@ void RM_ZsetRangeStop(RedisModuleKey *key) { | |||
| 
 | ||||
| /* Return the "End of range" flag value to signal the end of the iteration. */ | ||||
| int RM_ZsetRangeEndReached(RedisModuleKey *key) { | ||||
|     if (!key->value || key->value->type != OBJ_ZSET) return 1; | ||||
|     return key->zer; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ TEST_MODULES = \ | |||
|     auth.so \
 | ||||
|     keyspace_events.so \
 | ||||
|     blockedclient.so \
 | ||||
|     zset.so \
 | ||||
|     getkeys.so | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,30 @@ | |||
| #include "redismodule.h" | ||||
| 
 | ||||
| /* ZSET.REM key element
 | ||||
|  * | ||||
|  * Removes an occurrence of an element from a sorted set. Replies with the | ||||
|  * number of removed elements (0 or 1). | ||||
|  */ | ||||
| int zset_rem(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { | ||||
|     if (argc != 3) return RedisModule_WrongArity(ctx); | ||||
|     RedisModule_AutoMemory(ctx); | ||||
|     int keymode = REDISMODULE_READ | REDISMODULE_WRITE; | ||||
|     RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], keymode); | ||||
|     int deleted; | ||||
|     if (RedisModule_ZsetRem(key, argv[2], &deleted) == REDISMODULE_OK) | ||||
|         return RedisModule_ReplyWithLongLong(ctx, deleted); | ||||
|     else | ||||
|         return RedisModule_ReplyWithError(ctx, "ERR ZsetRem failed"); | ||||
| } | ||||
| 
 | ||||
| int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { | ||||
|     REDISMODULE_NOT_USED(argv); | ||||
|     REDISMODULE_NOT_USED(argc); | ||||
|     if (RedisModule_Init(ctx, "zset", 1, REDISMODULE_APIVER_1) == | ||||
|         REDISMODULE_OK && | ||||
|         RedisModule_CreateCommand(ctx, "zset.rem", zset_rem, "", | ||||
|                                   1, 1, 1) == REDISMODULE_OK) | ||||
|         return REDISMODULE_OK; | ||||
|     else | ||||
|         return REDISMODULE_ERR; | ||||
| } | ||||
|  | @ -0,0 +1,16 @@ | |||
| set testmodule [file normalize tests/modules/zset.so] | ||||
| 
 | ||||
| start_server {tags {"modules"}} { | ||||
|     r module load $testmodule | ||||
| 
 | ||||
|     test {Module zset rem} { | ||||
|         r del k | ||||
|         r zadd k 100 hello 200 world | ||||
|         assert_equal 1 [r zset.rem k hello] | ||||
|         assert_equal 0 [r zset.rem k hello] | ||||
|         assert_equal 1 [r exists k] | ||||
|         # Check that removing the last element deletes the key | ||||
|         assert_equal 1 [r zset.rem k world] | ||||
|         assert_equal 0 [r exists k] | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue