int rl_key_set(rlite *db, const unsigned char *key, long keylen, unsigned char type, long value_page, unsigned long long expires, long version) { int retval; rl_key *key_obj = NULL; unsigned char *digest = NULL; RL_CALL2(rl_key_delete, RL_OK, RL_NOT_FOUND, db, key, keylen); RL_MALLOC(digest, sizeof(unsigned char) * 20); RL_CALL(sha1, RL_OK, key, keylen, digest); rl_btree *btree; RL_CALL(rl_get_key_btree, RL_OK, db, &btree, 1); RL_MALLOC(key_obj, sizeof(*key_obj)) RL_CALL(rl_multi_string_set, RL_OK, db, &key_obj->string_page, key, keylen); key_obj->type = type; key_obj->value_page = value_page; key_obj->expires = expires; // reserving version=0 for non existent keys if (version == 0) { version = 1; } key_obj->version = version; RL_CALL(rl_btree_add_element, RL_OK, db, btree, db->databases[rl_get_selected_db(db)], digest, key_obj); retval = RL_OK; cleanup: if (retval != RL_OK) { rl_free(digest); rl_free(key_obj); } return retval; }
int rl_flushdb(struct rlite *db) { int retval; rl_btree *btree; rl_btree_iterator *iterator = NULL; rl_key *key; void *tmp; RL_CALL2(rl_get_key_btree, RL_OK, RL_NOT_FOUND, db, &btree, 0); if (retval == RL_NOT_FOUND) { retval = RL_OK; goto cleanup; } RL_CALL(rl_btree_iterator_create, RL_OK, db, btree, &iterator); while ((retval = rl_btree_iterator_next(iterator, NULL, &tmp)) == RL_OK) { key = tmp; RL_CALL(rl_key_delete_value, RL_OK, db, key->type, key->value_page); RL_CALL(rl_multi_string_delete, RL_OK, db, key->string_page); rl_free(key); } if (retval != RL_END) { goto cleanup; } RL_CALL(rl_btree_delete, RL_OK, db, btree); RL_CALL(rl_delete, RL_OK, db, db->databases[db->selected_database]); db->databases[db->selected_database] = 0; RL_CALL(rl_write, RL_OK, db, &rl_data_type_header, 0, NULL); retval = RL_OK; cleanup: return retval; }
int rl_rename(struct rlite *db, const unsigned char *src, long srclen, const unsigned char *target, long targetlen, int overwrite) { int retval; unsigned char type; unsigned long long expires; long value_page; long version = 0; if (overwrite) { RL_CALL2(rl_key_get, RL_FOUND, RL_NOT_FOUND, db, target, targetlen, NULL, NULL, NULL, NULL, &version); if (retval == RL_FOUND) { RL_CALL(rl_key_delete_with_value, RL_OK, db, target, targetlen); version++; } } else { RL_CALL(rl_key_get, RL_NOT_FOUND, db, target, targetlen, NULL, NULL, NULL, NULL, NULL); } // this could be more efficient, if we don't delete the value page RL_CALL(rl_key_get, RL_FOUND, db, src, srclen, &type, NULL, &value_page, &expires, NULL); RL_CALL(rl_key_delete, RL_OK, db, src, srclen); RL_CALL(rl_key_set, RL_OK, db, target, targetlen, type, value_page, expires, version); retval = RL_OK; cleanup: return retval; }
static int rl_key_get_ignore_expire(struct rlite *db, const unsigned char *key, long keylen, unsigned char *type, long *string_page, long *value_page, unsigned long long *expires, long *version, int ignore_expire) { unsigned char digest[20]; int retval; RL_CALL(sha1, RL_OK, key, keylen, digest); RL_CALL2(rl_key_get_hash_ignore_expire, RL_FOUND, RL_DELETED, db, digest, type, string_page, value_page, expires, version, ignore_expire); if (retval == RL_DELETED) { rl_key_delete_with_value(db, key, keylen); if (version) { *version = 0; } retval = RL_NOT_FOUND; } cleanup: return retval; }
int rl_watch(struct rlite *db, struct watched_key** _watched_key, const unsigned char *key, long keylen) { int retval; struct watched_key* wkey = NULL; RL_MALLOC(wkey, sizeof(struct watched_key)); wkey->database = rl_get_selected_db(db); RL_CALL(sha1, RL_OK, key, keylen, wkey->digest); RL_CALL2(rl_key_get_hash_ignore_expire, RL_FOUND, RL_NOT_FOUND, db, wkey->digest, NULL, NULL, NULL, NULL, &wkey->version, 1); if (retval == RL_NOT_FOUND) { wkey->version = 0; } *_watched_key = wkey; retval = RL_OK; cleanup: if (retval != RL_OK) { rl_free(wkey); } return retval; }
static int rl_key_sha_check_version(struct rlite *db, struct watched_key* key) { int retval; long version; // if the key has expired, the version is still valid, according to // https://code.google.com/p/redis/issues/detail?id=270 // it seems to be relevant to redis being stateful and single process // I don't think it is possible to replicate exactly the behavior, but // this is pretty close. RL_CALL2(rl_key_get_hash_ignore_expire, RL_FOUND, RL_NOT_FOUND, db, key->digest, NULL, NULL, NULL, NULL, &version, 1); if (retval == RL_NOT_FOUND) { version = 0; } if (version != key->version) { retval = RL_OUTDATED; } else { retval = RL_OK; } cleanup: return retval; }
int rl_zinterstore(rlite *db, long keys_size, unsigned char **keys, long *keys_len, double *_weights, int aggregate) { unsigned char *member = NULL; rl_btree **btrees = NULL; rl_skiplist **skiplists = NULL; double weight = 1.0, weight_tmp; double *weights = NULL; rl_skiplist_node *node; rl_skiplist_iterator *skiplist_iterator = NULL; rl_btree_iterator *btree_iterator = NULL; int retval; long target_btree_page, target_skiplist_page; long multi_string_page; rl_btree *target_btree; rl_skiplist *target_skiplist; int found; void *tmp; double skiplist_score, tmp_score; long memberlen; unsigned char digest[20]; if (keys_size > 1) { RL_MALLOC(btrees, sizeof(rl_btree *) * (keys_size - 1)); RL_MALLOC(skiplists, sizeof(rl_skiplist *) * (keys_size - 1)); } else { retval = RL_UNEXPECTED; goto cleanup; } retval = rl_key_delete_with_value(db, keys[0], keys_len[0]); if (retval != RL_OK && retval != RL_NOT_FOUND) { goto cleanup; } rl_btree *btree, *btree_tmp; rl_skiplist *skiplist = NULL, *skiplist_tmp; long i; // key in position 0 is the target key // we'll store a pivot skiplist in btree/skiplist and the others in btrees/skiplists // TODO: qsort instead RL_MALLOC(weights, sizeof(double) * (keys_size - 2)); for (i = 1; i < keys_size; i++) { weight_tmp = _weights ? _weights[i - 1] : 1.0; RL_CALL2(rl_zset_get_objects, RL_OK, RL_WRONG_TYPE, db, keys[i], keys_len[i], NULL, &btree_tmp, NULL, &skiplist_tmp, NULL, 0, 0); if (retval == RL_WRONG_TYPE) { skiplist_tmp = NULL; RL_CALL(rl_set_get_objects, RL_OK, db, keys[i], keys_len[i], NULL, &btree_tmp, 0, 0); } if (i == 1) { btree = btree_tmp; skiplist = skiplist_tmp; weight = weight_tmp; } else if (btree_tmp->number_of_elements < btree->number_of_elements) { weights[i - 2] = weight; weight = weight_tmp; btrees[i - 2] = btree; btree = btree_tmp; skiplists[i - 2] = skiplist; skiplist = skiplist_tmp; } else { weights[i - 2] = weight_tmp; btrees[i - 2] = btree_tmp; skiplists[i - 2] = skiplist_tmp; } } RL_CALL(rl_zset_get_objects, RL_OK, db, keys[0], keys_len[0], NULL, &target_btree, &target_btree_page, &target_skiplist, &target_skiplist_page, 1, 1); if (skiplist) { RL_CALL(rl_skiplist_iterator_create, RL_OK, db, &skiplist_iterator, skiplist, 0, 0, 0); } else { RL_CALL(rl_btree_iterator_create, RL_OK, db, btree, &btree_iterator); } while ((retval = skiplist ? rl_skiplist_iterator_next(skiplist_iterator, &node) : rl_btree_iterator_next(btree_iterator, NULL, &tmp)) == RL_OK) { found = 1; if (skiplist) { skiplist_score = node->score * weight; multi_string_page = node->value; } else { skiplist_score = weight; multi_string_page = *(long *)tmp; rl_free(tmp); } for (i = 1; i < keys_size - 1; i++) { RL_CALL(rl_multi_string_sha1, RL_OK, db, digest, multi_string_page); retval = rl_btree_find_score(db, btrees[i - 1], digest, &tmp, NULL, NULL); if (retval == RL_NOT_FOUND) { found = 0; break; } else if (retval == RL_FOUND) { tmp_score = skiplist ? *(double *)tmp : 1.0; if (aggregate == RL_ZSET_AGGREGATE_SUM) { skiplist_score += tmp_score * weights[i - 1]; } else if ( (aggregate == RL_ZSET_AGGREGATE_MIN && tmp_score * weights[i - 1] < skiplist_score) || (aggregate == RL_ZSET_AGGREGATE_MAX && tmp_score * weights[i - 1] > skiplist_score) ) { skiplist_score = tmp_score * weights[i - 1]; } } else { goto cleanup; } } if (found) { RL_CALL(rl_multi_string_get, RL_OK, db, multi_string_page, &member, &memberlen); RL_CALL(add_member, RL_OK, db, target_btree, target_btree_page, target_skiplist, target_skiplist_page, isnan(skiplist_score) ? 0.0 : skiplist_score, member, memberlen); rl_free(member); member = NULL; } } skiplist_iterator = NULL; btree_iterator = NULL; if (retval != RL_END) { goto cleanup; } retval = RL_OK; cleanup: rl_free(member); if (skiplist_iterator) { rl_zset_iterator_destroy(skiplist_iterator); } if (btree_iterator) { rl_btree_iterator_destroy(btree_iterator); } rl_free(weights); rl_free(btrees); rl_free(skiplists); return retval; }