| 1173 | * of every element on the Hash. */ |
| 1174 | void scanFilterAndReply(client *c, list *keys, sds pat, sds type, int use_pattern, robj_roptr o, unsigned long cursor); |
| 1175 | void scanGenericCommand(client *c, robj_roptr o, unsigned long cursor) { |
| 1176 | int i, j; |
| 1177 | list *keys = listCreate(); |
| 1178 | long count = 10; |
| 1179 | sds pat = NULL; |
| 1180 | sds type = NULL; |
| 1181 | int patlen = 0, use_pattern = 0; |
| 1182 | dict *ht; |
| 1183 | |
| 1184 | /* Object must be NULL (to iterate keys names), or the type of the object |
| 1185 | * must be Set, Sorted Set, or Hash. */ |
| 1186 | serverAssert(o == nullptr || o->type == OBJ_SET || o->type == OBJ_HASH || |
| 1187 | o->type == OBJ_ZSET); |
| 1188 | |
| 1189 | /* Set i to the first option argument. The previous one is the cursor. */ |
| 1190 | i = (o == nullptr) ? 2 : 3; /* Skip the key argument if needed. */ |
| 1191 | |
| 1192 | /* Step 1: Parse options. */ |
| 1193 | while (i < c->argc) { |
| 1194 | j = c->argc - i; |
| 1195 | if (!strcasecmp(szFromObj(c->argv[i]), "count") && j >= 2) { |
| 1196 | if (getLongFromObjectOrReply(c, c->argv[i+1], &count, NULL) |
| 1197 | != C_OK) |
| 1198 | { |
| 1199 | goto cleanup; |
| 1200 | } |
| 1201 | |
| 1202 | if (count < 1) { |
| 1203 | addReplyErrorObject(c,shared.syntaxerr); |
| 1204 | goto cleanup; |
| 1205 | } |
| 1206 | |
| 1207 | i += 2; |
| 1208 | } else if (!strcasecmp(szFromObj(c->argv[i]), "match") && j >= 2) { |
| 1209 | pat = szFromObj(c->argv[i+1]); |
| 1210 | patlen = sdslen(pat); |
| 1211 | |
| 1212 | /* The pattern always matches if it is exactly "*", so it is |
| 1213 | * equivalent to disabling it. */ |
| 1214 | use_pattern = !(pat[0] == '*' && patlen == 1); |
| 1215 | |
| 1216 | i += 2; |
| 1217 | } else if (!strcasecmp(szFromObj(c->argv[i]), "type") && o == nullptr && j >= 2) { |
| 1218 | /* SCAN for a particular type only applies to the db dict */ |
| 1219 | type = szFromObj(c->argv[i+1]); |
| 1220 | i+= 2; |
| 1221 | } else { |
| 1222 | addReplyErrorObject(c,shared.syntaxerr); |
| 1223 | goto cleanup; |
| 1224 | } |
| 1225 | } |
| 1226 | |
| 1227 | if (o == nullptr && count >= 100 && !(serverTL->in_eval || serverTL->in_exec)) |
| 1228 | { |
| 1229 | // Do an async version |
| 1230 | if (c->asyncCommand( |
| 1231 | [c, keys, pat, type, cursor, count, use_pattern] (const redisDbPersistentDataSnapshot *snapshot, const std::vector<robj_sharedptr> &) { |
| 1232 | sds patCopy = pat ? sdsdup(pat) : nullptr; |
no test coverage detected