/* * adds a delta value to a numeric item. * * c connection requesting the operation * it item to adjust * incr true to increment value, false to decrement * delta amount to adjust value by * @param ritem The resulting item after adding the delta. Only valid if * ENGINE_SUCCESS is returned. Caller is responsible for calling * do_item_release() on this when finished with it. * * returns a response code to send back to the client. */ static ENGINE_ERROR_CODE do_add_delta(struct default_engine *engine, hash_item *it, const bool incr, const int64_t delta, item** ritem, uint64_t *result, const void *cookie) { const char *ptr; uint64_t value; char buf[80]; int res; if (it->nbytes >= (sizeof(buf) - 1)) { return ENGINE_EINVAL; } ptr = item_get_data(it); memcpy(buf, ptr, it->nbytes); buf[it->nbytes] = '\0'; if (!safe_strtoull(buf, &value)) { return ENGINE_EINVAL; } if (incr) { value += delta; } else { if ((uint64_t)delta > value) { value = 0; } else { value -= delta; } } *result = value; res = snprintf(buf, sizeof(buf), "%" PRIu64, value); if (res < 0 || res >= sizeof(buf)) { return ENGINE_EINVAL; } if (it->refcount == 1 && res <= (int)it->nbytes) { /* we can do inline replacement */ memcpy(item_get_data(it), buf, res); memset(item_get_data(it) + res, ' ', it->nbytes - res); item_set_cas(NULL, NULL, it, get_cas_id()); *ritem = it; } else { hash_item *new_it = do_item_alloc(engine, item_get_key(it), it->flags, it->exptime, res, cookie, it->datatype); if (new_it == NULL) { do_item_unlink(engine, it); return ENGINE_ENOMEM; } memcpy(item_get_data(new_it), buf, res); do_item_replace(engine, it, new_it); *ritem = new_it; } return ENGINE_SUCCESS; }
static ENGINE_ERROR_CODE mock_arithmetic(ENGINE_HANDLE* handle, const void* cookie, const void* key, const int nkey, const bool increment, const bool create, const uint64_t delta, const uint64_t initial, const rel_time_t exptime, uint64_t *cas, uint64_t *result, uint16_t vbucket) { (void)increment; (void)vbucket; item *item_in = NULL, *item_out = NULL; int flags = 0; *cas = 0; if (mock_get(handle, cookie, &item_in, key, nkey, 0) == ENGINE_SUCCESS) { // Found, just do the math. // This is all int stuff, just to make it easy. *result = atoi(item_get_data(item_in)); *result += delta; flags = ((mock_item*) item_in)->flags; } else if (create) { // Not found, do the initialization *result = initial; } else { // Reject. return ENGINE_KEY_ENOENT; } char buf[32]; snprintf(buf, sizeof(buf), "%"PRIu64, *result); ENGINE_ERROR_CODE rv; if((rv = mock_item_allocate(handle, cookie, &item_out, key, nkey, strlen(buf) + 1, flags, exptime)) != ENGINE_SUCCESS) { return rv; } memcpy(item_get_data(item_out), buf, strlen(buf) + 1); mock_store(handle, cookie, item_out, 0, OPERATION_SET, 0); return ENGINE_SUCCESS; }
static ENGINE_ERROR_CODE do_arithmetic(struct default_engine *engine, const void* cookie, const hash_key* key, const bool increment, const bool create, const uint64_t delta, const uint64_t initial, const rel_time_t exptime, item **result_item, uint8_t datatype, uint64_t *result) { hash_item *item = do_item_get(engine, key); ENGINE_ERROR_CODE ret; if (item == NULL) { if (!create) { return ENGINE_KEY_ENOENT; } else { char buffer[128]; int len = snprintf(buffer, sizeof(buffer), "%"PRIu64, (uint64_t)initial); if (len < 0 || len >= sizeof(buffer)) { return ENGINE_ENOMEM; } item = do_item_alloc(engine, key, 0, exptime, len, cookie, datatype); if (item == NULL) { return ENGINE_ENOMEM; } memcpy((void*)item_get_data(item), buffer, len); if ((ret = do_store_item(engine, item, OPERATION_ADD, cookie, (hash_item**)result_item)) == ENGINE_SUCCESS) { *result = initial; } else { do_item_release(engine, item); } } } else { ret = do_add_delta(engine, item, increment, delta, result_item, result, cookie); } return ret; }
static bool get_item_info(ENGINE_HANDLE *handle, const void *cookie, const item* item, item_info *item_info) { mock_item* it = (mock_item*)item; if (item_info->nvalue < 1) { return false; } item_info->cas = item_get_cas(it); item_info->exptime = it->exptime; item_info->nbytes = it->nbytes; item_info->flags = it->flags; item_info->clsid = it->clsid; item_info->nkey = it->nkey; item_info->nvalue = 1; item_info->key = item_get_key(it); item_info->value[0].iov_base = item_get_data(it); item_info->value[0].iov_len = it->nbytes; return true; }
/* * Stores an item in the cache according to the semantics of one of the set * commands. In threaded mode, this is protected by the cache lock. * * Returns the state of storage. */ static ENGINE_ERROR_CODE do_store_item(struct default_engine *engine, hash_item *it, uint64_t *cas, ENGINE_STORE_OPERATION operation, const void *cookie) { const char *key = item_get_key(it); hash_item *old_it = do_item_get(engine, key, it->nkey); ENGINE_ERROR_CODE stored = ENGINE_NOT_STORED; hash_item *new_it = NULL; if (old_it != NULL && operation == OPERATION_ADD) { /* add only adds a nonexistent item, but promote to head of LRU */ do_item_update(engine, old_it); } else if (!old_it && (operation == OPERATION_REPLACE || operation == OPERATION_APPEND || operation == OPERATION_PREPEND)) { /* replace only replaces an existing value; don't store */ } else if (operation == OPERATION_CAS) { /* validate cas operation */ if(old_it == NULL) { // LRU expired stored = ENGINE_KEY_ENOENT; } else if (item_get_cas(it) == item_get_cas(old_it)) { // cas validates // it and old_it may belong to different classes. // I'm updating the stats for the one that's getting pushed out do_item_replace(engine, old_it, it); stored = ENGINE_SUCCESS; } else { if (engine->config.verbose > 1) { EXTENSION_LOGGER_DESCRIPTOR *logger; logger = (void*)engine->server.extension->get_extension(EXTENSION_LOGGER); logger->log(EXTENSION_LOG_INFO, NULL, "CAS: failure: expected %"PRIu64", got %"PRIu64"\n", item_get_cas(old_it), item_get_cas(it)); } stored = ENGINE_KEY_EEXISTS; } } else { /* * Append - combine new and old record into single one. Here it's * atomic and thread-safe. */ if (operation == OPERATION_APPEND || operation == OPERATION_PREPEND) { /* * Validate CAS */ if (item_get_cas(it) != 0) { // CAS much be equal if (item_get_cas(it) != item_get_cas(old_it)) { stored = ENGINE_KEY_EEXISTS; } } if (stored == ENGINE_NOT_STORED) { /* we have it and old_it here - alloc memory to hold both */ new_it = do_item_alloc(engine, key, it->nkey, old_it->flags, old_it->exptime, it->nbytes + old_it->nbytes, cookie); if (new_it == NULL) { /* SERVER_ERROR out of memory */ if (old_it != NULL) { do_item_release(engine, old_it); } return ENGINE_NOT_STORED; } /* copy data from it and old_it to new_it */ if (operation == OPERATION_APPEND) { memcpy(item_get_data(new_it), item_get_data(old_it), old_it->nbytes); memcpy(item_get_data(new_it) + old_it->nbytes, item_get_data(it), it->nbytes); } else { /* OPERATION_PREPEND */ memcpy(item_get_data(new_it), item_get_data(it), it->nbytes); memcpy(item_get_data(new_it) + it->nbytes, item_get_data(old_it), old_it->nbytes); } it = new_it; } } if (stored == ENGINE_NOT_STORED) { if (old_it != NULL) { do_item_replace(engine, old_it, it); } else { do_item_link(engine, it); } *cas = item_get_cas(it); stored = ENGINE_SUCCESS; } } if (old_it != NULL) { do_item_release(engine, old_it); /* release our reference */ } if (new_it != NULL) { do_item_release(engine, new_it); } if (stored == ENGINE_SUCCESS) { *cas = item_get_cas(it); } return stored; }