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/keyspace_events \ | ||||||
| --single unit/moduleapi/blockedclient \ | --single unit/moduleapi/blockedclient \ | ||||||
| --single unit/moduleapi/getkeys \ | --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 && key->value->type != OBJ_ZSET) return REDISMODULE_ERR; | ||||||
|     if (key->value != NULL && zsetDel(key->value,ele->ptr)) { |     if (key->value != NULL && zsetDel(key->value,ele->ptr)) { | ||||||
|         if (deleted) *deleted = 1; |         if (deleted) *deleted = 1; | ||||||
|  |         moduleDelKeyIfEmpty(key); | ||||||
|     } else { |     } else { | ||||||
|         if (deleted) *deleted = 0; |         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. */ | /* Return the "End of range" flag value to signal the end of the iteration. */ | ||||||
| int RM_ZsetRangeEndReached(RedisModuleKey *key) { | int RM_ZsetRangeEndReached(RedisModuleKey *key) { | ||||||
|  |     if (!key->value || key->value->type != OBJ_ZSET) return 1; | ||||||
|     return key->zer; |     return key->zer; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ TEST_MODULES = \ | ||||||
|     auth.so \
 |     auth.so \
 | ||||||
|     keyspace_events.so \
 |     keyspace_events.so \
 | ||||||
|     blockedclient.so \
 |     blockedclient.so \
 | ||||||
|  |     zset.so \
 | ||||||
|     getkeys.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