This command implements SCAN, HSCAN and SSCAN commands. * If object 'o' is passed, then it must be a Hash, Set or Zset object, otherwise * if 'o' is NULL the command will operate on the dictionary associated with * the current database. * * When 'o' is not NULL the function assumes that the first argument in * the client arguments vector is a key so it skips it before iterating * in order t
| 835 | * In the case of a Hash object the function returns both the field and value |
| 836 | * of every element on the Hash. */ |
| 837 | void scanGenericCommand(client *c, robj *o, unsigned long cursor) { |
| 838 | int i, j; |
| 839 | list *keys = listCreate(); |
| 840 | listNode *node, *nextnode; |
| 841 | long count = 10; |
| 842 | sds pat = NULL; |
| 843 | sds typename = NULL; |
| 844 | int patlen = 0, use_pattern = 0; |
| 845 | dict *ht; |
| 846 | |
| 847 | /* Object must be NULL (to iterate keys names), or the type of the object |
| 848 | * must be Set, Sorted Set, or Hash. */ |
| 849 | serverAssert(o == NULL || o->type == OBJ_SET || o->type == OBJ_HASH || |
| 850 | o->type == OBJ_ZSET); |
| 851 | |
| 852 | /* Set i to the first option argument. The previous one is the cursor. */ |
| 853 | i = (o == NULL) ? 2 : 3; /* Skip the key argument if needed. */ |
| 854 | |
| 855 | /* Step 1: Parse options. */ |
| 856 | while (i < c->argc) { |
| 857 | j = c->argc - i; |
| 858 | if (!strcasecmp(c->argv[i]->ptr, "count") && j >= 2) { |
| 859 | if (getLongFromObjectOrReply(c, c->argv[i+1], &count, NULL) |
| 860 | != C_OK) |
| 861 | { |
| 862 | goto cleanup; |
| 863 | } |
| 864 | |
| 865 | if (count < 1) { |
| 866 | addReplyErrorObject(c,shared.syntaxerr); |
| 867 | goto cleanup; |
| 868 | } |
| 869 | |
| 870 | i += 2; |
| 871 | } else if (!strcasecmp(c->argv[i]->ptr, "match") && j >= 2) { |
| 872 | pat = c->argv[i+1]->ptr; |
| 873 | patlen = sdslen(pat); |
| 874 | |
| 875 | /* The pattern always matches if it is exactly "*", so it is |
| 876 | * equivalent to disabling it. */ |
| 877 | use_pattern = !(pat[0] == '*' && patlen == 1); |
| 878 | |
| 879 | i += 2; |
| 880 | } else if (!strcasecmp(c->argv[i]->ptr, "type") && o == NULL && j >= 2) { |
| 881 | /* SCAN for a particular type only applies to the db dict */ |
| 882 | typename = c->argv[i+1]->ptr; |
| 883 | i+= 2; |
| 884 | } else { |
| 885 | addReplyErrorObject(c,shared.syntaxerr); |
| 886 | goto cleanup; |
| 887 | } |
| 888 | } |
| 889 | |
| 890 | /* Step 2: Iterate the collection. |
| 891 | * |
| 892 | * Note that if the object is encoded with a ziplist, intset, or any other |
| 893 | * representation that is not a hash table, we are sure that it is also |
| 894 | * composed of a small number of elements. So to avoid taking state we |
no test coverage detected