/* Return the value associated to the key with a name obtained using * the following rules: * * 1) The first occurrence of '*' in 'pattern' is substituted with 'subst'. * * 2) If 'pattern' matches the "->" string, everything on the left of * the arrow is treated as the name of a hash field, and the part on the * left as the key name containing a hash. The value of the specified * field is returned. * * 3) If 'pattern' equals "#", the function simply returns 'subst' itself so * that the SORT command can be used like: SORT key GET # to retrieve * the Set/List elements directly. * * The returned object will always have its refcount increased by 1 * when it is non-NULL. */ robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) { char *p, *f, *k; sds spat, ssub; robj *keyobj, *fieldobj = NULL, *o; int prefixlen, sublen, postfixlen, fieldlen; /* If the pattern is "#" return the substitution object itself in order * to implement the "SORT ... GET #" feature. */ spat = pattern->ptr; if (spat[0] == '#' && spat[1] == '\0') { incrRefCount(subst); return subst; } /* The substitution object may be specially encoded. If so we create * a decoded object on the fly. Otherwise getDecodedObject will just * increment the ref count, that we'll decrement later. */ subst = getDecodedObject(subst); ssub = subst->ptr; /* If we can't find '*' in the pattern we return NULL as to GET a * fixed key does not make sense. */ p = strchr(spat,'*'); if (!p) { decrRefCount(subst); return NULL; } /* Find out if we're dealing with a hash dereference. */ if ((f = strstr(p+1, "->")) != NULL && *(f+2) != '\0') { fieldlen = sdslen(spat)-(f-spat)-2; fieldobj = createStringObject(f+2,fieldlen); } else { fieldlen = 0; } /* Perform the '*' substitution. */ prefixlen = p-spat; sublen = sdslen(ssub); postfixlen = sdslen(spat)-(prefixlen+1)-(fieldlen ? fieldlen+2 : 0); keyobj = createStringObject(NULL,prefixlen+sublen+postfixlen); k = keyobj->ptr; memcpy(k,spat,prefixlen); memcpy(k+prefixlen,ssub,sublen); memcpy(k+prefixlen+sublen,p+1,postfixlen); decrRefCount(subst); /* Incremented by decodeObject() */ /* Lookup substituted key */ o = lookupKeyRead(db,keyobj); if (o == NULL) goto noobj; if (fieldobj) { if (o->type != REDIS_HASH) goto noobj; /* Retrieve value from hash by the field name. This operation * already increases the refcount of the returned object. */ o = hashTypeGetObject(o, fieldobj); } else { if (o->type != REDIS_STRING) goto noobj; /* Every object that this function returns needs to have its refcount * increased. sortCommand decreases it again. */ incrRefCount(o); } decrRefCount(keyobj); if (fieldobj) decrRefCount(fieldobj); return o; noobj: decrRefCount(keyobj); if (fieldlen) decrRefCount(fieldobj); return NULL; }
/* Return the value associated to the key with a name obtained * substituting the first occurence of '*' in 'pattern' with 'subst'. * The returned object will always have its refcount increased by 1 * when it is non-NULL. */ robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) { char *p, *f; sds spat, ssub; robj keyobj, fieldobj, *o; int prefixlen, sublen, postfixlen, fieldlen; /* Expoit the internal sds representation to create a sds string allocated on the stack in order to make this function faster */ struct { int len; int free; char buf[REDIS_SORTKEY_MAX+1]; } keyname, fieldname; /* If the pattern is "#" return the substitution object itself in order * to implement the "SORT ... GET #" feature. */ spat = pattern->ptr; if (spat[0] == '#' && spat[1] == '\0') { incrRefCount(subst); return subst; } /* The substitution object may be specially encoded. If so we create * a decoded object on the fly. Otherwise getDecodedObject will just * increment the ref count, that we'll decrement later. */ subst = getDecodedObject(subst); ssub = subst->ptr; if (sdslen(spat)+sdslen(ssub)-1 > REDIS_SORTKEY_MAX) return NULL; p = strchr(spat,'*'); if (!p) { decrRefCount(subst); return NULL; } /* Find out if we're dealing with a hash dereference. */ if ((f = strstr(p+1, "->")) != NULL) { fieldlen = (int)(sdslen(spat)-(f-spat)); /* this also copies \0 character */ memcpy(fieldname.buf,f+2,fieldlen-1); fieldname.len = fieldlen-2; } else { fieldlen = 0; } prefixlen = (int)(p-spat); sublen = (int)sdslen(ssub); postfixlen = (int)(sdslen(spat)-(prefixlen+1)-fieldlen); memcpy(keyname.buf,spat,prefixlen); memcpy(keyname.buf+prefixlen,ssub,sublen); memcpy(keyname.buf+prefixlen+sublen,p+1,postfixlen); keyname.buf[prefixlen+sublen+postfixlen] = '\0'; keyname.len = prefixlen+sublen+postfixlen; decrRefCount(subst); /* Lookup substituted key */ initStaticStringObject(keyobj,((char*)&keyname)+(sizeof(struct sdshdr))); o = lookupKeyRead(db,&keyobj); if (o == NULL) return NULL; if (fieldlen > 0) { if (o->type != REDIS_HASH || fieldname.len < 1) return NULL; /* Retrieve value from hash by the field name. This operation * already increases the refcount of the returned object. */ initStaticStringObject(fieldobj,((char*)&fieldname)+(sizeof(struct sdshdr))); o = hashTypeGetObject(o, &fieldobj); } else { if (o->type != REDIS_STRING) return NULL; /* Every object that this function returns needs to have its refcount * increased. sortCommand decreases it again. */ incrRefCount(o); } return o; }