int Ardb::Keys(Context& ctx, RedisCommandFrame& cmd) { KeysOptions opt; opt.op = OP_GET; opt.pattern = cmd.GetArguments()[0]; uint32 limit = 100000; //return max 100000 keys one time if (cmd.GetArguments().size() > 1) { for (uint32 i = 1; i < cmd.GetArguments().size(); i++) { if (!strcasecmp(cmd.GetArguments()[i].c_str(), "limit")) { if (i + 1 >= cmd.GetArguments().size() || !string_touint32(cmd.GetArguments()[i + 1], limit)) { fill_error_reply(ctx.reply, "value is not an integer or out of range"); return 0; } i++; } else { fill_error_reply(ctx.reply, "Syntax error, try KEYS "); return 0; } } } opt.limit = limit; KeysOperation(ctx, opt); return 0; }
/* * GEOADD key MERCATOR|WGS84 x y value [x y value....] */ int Ardb::GeoAdd(Context& ctx, RedisCommandFrame& cmd) { GeoAddOptions options; std::string err; if (0 != options.Parse(cmd.GetArguments(), err, 1)) { fill_error_reply(ctx.reply, "%s", err.c_str()); return 0; } GeoHashRange lat_range, lon_range; GeoHashHelper::GetCoordRange(options.coord_type, lat_range, lon_range); KeyLockerGuard keylock(m_key_lock, ctx.currentDB, cmd.GetArguments()[0]); ValueObject meta; int ret = GetMetaValue(ctx, cmd.GetArguments()[0], ZSET_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, ret); RedisCommandFrame zadd("zadd"); zadd.AddArg(cmd.GetArguments()[0]); BatchWriteGuard guard(GetKeyValueEngine()); GeoPointArray::iterator git = options.points.begin(); uint32 count = 0; while (git != options.points.end()) { GeoPoint& point = *git; if (point.x < lon_range.min || point.x > lon_range.max || point.y < lat_range.min || point.y > lat_range.max) { guard.MarkFailed(); break; } GeoHashBits hash; geohash_encode(&lat_range, &lon_range, point.y, point.x, 30, &hash); GeoHashFix60Bits score = hash.bits; Data score_value; score_value.SetInt64((int64) score); Data element; element.SetString(point.value, true); count += ZSetAdd(ctx, meta, element, score_value, NULL); std::string tmp; score_value.GetDecodeString(tmp); zadd.AddArg(tmp); element.GetDecodeString(tmp); zadd.AddArg(tmp); git++; } if (guard.Success()) { SetKeyValue(ctx, meta); RewriteClientCommand(ctx, zadd); fill_int_reply(ctx.reply, count); } else { fill_error_reply(ctx.reply, "Invalid arguments"); } return 0; }
int Ardb::BRPop(Context& ctx, RedisCommandFrame& cmd) { uint32 timeout; if (!string_touint32(cmd.GetArguments()[cmd.GetArguments().size() - 1], timeout)) { fill_error_reply(ctx.reply, "timeout is not an integer or out of range"); return 0; } for (uint32 i = 0; i < cmd.GetArguments().size() - 1; i++) { if (ListPop(ctx, cmd.GetArguments()[i], false) == 0 && ctx.reply.type == REDIS_REPLY_STRING) { RedisReply& r1 = ctx.reply.AddMember(); RedisReply& r2 = ctx.reply.AddMember(); fill_str_reply(r1, cmd.GetArguments()[i]); fill_str_reply(r2, ctx.reply.str); return 0; } } for (uint32 i = 0; i < cmd.GetArguments().size() - 1; i++) { AddBlockKey(ctx, cmd.GetArguments()[i]); } if (NULL != ctx.client) { ctx.client->DetachFD(); if (timeout > 0) { ctx.block->blocking_timer_task_id = ctx.client->GetService().GetTimer().ScheduleHeapTask( new BlockConnectionTimeout(&ctx), timeout, -1, SECONDS); } } return 0; }
int Ardb::BRPopLPush(Context& ctx, RedisCommandFrame& cmd) { uint32 timeout; if (!string_touint32(cmd.GetArguments()[cmd.GetArguments().size() - 1], timeout)) { fill_error_reply(ctx.reply, "timeout is not an integer or out of range"); return 0; } RPopLPush(ctx, cmd); if (ctx.reply.type == REDIS_REPLY_NIL) { //block; AddBlockKey(ctx, cmd.GetArguments()[0]); if (NULL != ctx.client) { ctx.client->DetachFD(); if (timeout > 0) { ctx.block->blocking_timer_task_id = ctx.client->GetService().GetTimer().ScheduleHeapTask( new BlockConnectionTimeout(&ctx), timeout, -1, SECONDS); } } ctx.reply.type = 0; } return 0; }
int Ardb::HMSet(Context& ctx, RedisCommandFrame& cmd) { if (m_cfg.replace_for_hmset) { HReplace(ctx, cmd); return 0; } if ((cmd.GetArguments().size() - 1) % 2 != 0) { fill_error_reply(ctx.reply, "wrong number of arguments for HMSet"); return 0; } ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); DataMap fs; for (uint32 i = 1; i < cmd.GetArguments().size(); i += 2) { Data f(cmd.GetArguments()[i]), v(cmd.GetArguments()[i + 1]); fs[f] = v; } HashMultiSet(ctx, meta, fs); fill_status_reply(ctx.reply, "OK"); return 0; }
int Ardb::HIncrbyFloat(Context& ctx, RedisCommandFrame& cmd) { double increment, val = 0; if (!GetDoubleValue(ctx, cmd.GetArguments()[2], increment)) { return 0; } ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); Data value; Data field(cmd.GetArguments()[1]); if (0 == err) { HashGet(ctx, meta, field, value); if (!value.GetDouble(val)) { fill_error_reply(ctx.reply, "value is not a float or out of range"); return 0; } } val += increment; std::string tmp; fast_dtoa(val, 10, tmp); value.SetString(tmp, false); HashSet(ctx, meta, field, value); fill_str_reply(ctx.reply, tmp); return 0; }
int Ardb::HIncrby(Context& ctx, RedisCommandFrame& cmd) { int64 increment, val = 0; if (!GetInt64Value(ctx, cmd.GetArguments()[2], increment)) { return 0; } ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); Data field(cmd.GetArguments()[1]); Data value; if (err == 0) { err = HashGet(ctx, meta, field, value); } if (err == ERR_NOT_EXIST) { value.SetInt64(increment); } else { if (!value.GetInt64(val)) { fill_error_reply(ctx.reply, "value is not a integer or out of range"); return 0; } value.SetInt64(val + increment); } HashSet(ctx, meta, field, value); fill_int_reply(ctx.reply, val + increment); return 0; }
int Comms::LInsert(Context& ctx, RedisCommandFrame& cmd) { bool before_or_after = false; if (!strcasecmp(cmd.GetArguments()[1].c_str(), "before")) { before_or_after = true; } else if (!strcasecmp(cmd.GetArguments()[1].c_str(), "after")) { before_or_after = false; } else { fill_error_reply(ctx.reply, "Syntax error"); return 0; } int err = m_kv_store->LInsert(ctx.currentDB, cmd.GetArguments()[0], before_or_after, cmd.GetArguments()[2], cmd.GetArguments()[3]); if (err >= 0 || -1 == err) { fill_int_reply(ctx.reply, err); if (err > 0) { FireKeyChangedEvent(ctx, cmd.GetArguments()[0]); } } else { FillErrorReply(ctx, err); } return 0; }
int ArdbServer::Auth(ArdbConnContext& ctx, RedisCommandFrame& cmd) { if (m_cfg.requirepass.empty()) { fill_error_reply(ctx.reply, "Client sent AUTH, but no password is set"); } else if (m_cfg.requirepass != cmd.GetArguments()[0]) { ctx.authenticated = false; fill_error_reply(ctx.reply, "invalid password"); } else { ctx.authenticated = true; fill_status_reply(ctx.reply, "OK"); } return 0; }
int Ardb::HMIncrby(Context& ctx, RedisCommandFrame& cmd) { if ((cmd.GetArguments().size() - 1) % 2 != 0) { fill_error_reply(ctx.reply, "wrong number of arguments for HMIncrby"); return 0; } ValueObject meta; int err = GetMetaValue(ctx, cmd.GetArguments()[0], HASH_META, meta); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); ctx.reply.type = REDIS_REPLY_ARRAY; DataMap fs; Int64Array vs; for (uint32 i = 1; i < cmd.GetArguments().size(); i += 2) { int64 inc = 0; if (!GetInt64Value(ctx, cmd.GetArguments()[i + 1], inc)) { return 0; } Data field(cmd.GetArguments()[i]); if (err == ERR_NOT_EXIST) { fs[field].SetInt64(inc); vs.push_back(inc); } else { Data value; HashGet(ctx, meta, field, value); int64 val = 0; if (!value.GetInt64(val)) { fill_error_reply(ctx.reply, "value is not a float or out of range"); return 0; } fs[field].SetInt64(inc + val); vs.push_back(inc + val); } } HashMultiSet(ctx, meta, fs); fill_int_array_reply(ctx.reply, vs); return 0; }
int ArdbServer::Decrby(ArdbConnContext& ctx, RedisCommandFrame& cmd) { int64 decrement, val; if (!string_toint64(cmd.GetArguments()[1], decrement)) { fill_error_reply(ctx.reply, "value is not an integer or out of range"); return 0; } int ret = m_db->Decrby(ctx.currentDB, cmd.GetArguments()[0], decrement, val); if (ret == 0) { fill_int_reply(ctx.reply, val); } else { fill_error_reply(ctx.reply, "value is not an integer or out of range"); } return 0; }
int ArdbServer::IncrbyFloat(ArdbConnContext& ctx, RedisCommandFrame& cmd) { double increment, val; if (!string_todouble(cmd.GetArguments()[1], increment)) { fill_error_reply(ctx.reply, "value is not a float or out of range"); return 0; } int ret = m_db->IncrbyFloat(ctx.currentDB, cmd.GetArguments()[0], increment, val); if (ret == 0) { fill_double_reply(ctx.reply, val); } else { fill_error_reply(ctx.reply, "value is not a float or out of range"); } return 0; }
/* * CACHE LOAD|EVICT|STATUS key */ int Ardb::Cache(Context& ctx, RedisCommandFrame& cmd) { int ret = 0; if (!strcasecmp(cmd.GetArguments()[0].c_str(), "load")) { KeyType type = KEY_END; GetType(ctx, cmd.GetArguments()[1], type); if (type != KEY_END) { CacheLoadOptions options; options.deocode_geo = true; ret = m_cache.Load(ctx.currentDB, cmd.GetArguments()[1], type, options); } else { ret = ERR_INVALID_TYPE; } } else if (!strcasecmp(cmd.GetArguments()[0].c_str(), "evict")) { ret = m_cache.Evict(ctx.currentDB, cmd.GetArguments()[1]); } else if (!strcasecmp(cmd.GetArguments()[0].c_str(), "status")) { std::string status = m_cache.Status(ctx.currentDB, cmd.GetArguments()[1]); fill_str_reply(ctx.reply, status); return 0; } else { fill_error_reply(ctx.reply, "Syntax error, try CACHE (LOAD | EVICT | STATUS) key"); return 0; } if (ret == 0) { fill_status_reply(ctx.reply, "OK"); } else { fill_error_reply(ctx.reply, "Failed to cache load/evict/status key:%s", cmd.GetArguments()[1].c_str()); } return 0; }
int ArdbServer::Select(ArdbConnContext& ctx, RedisCommandFrame& cmd) { if (!string_touint32(cmd.GetArguments()[0], ctx.currentDB) || ctx.currentDB > 0xFFFFFF) { fill_error_reply(ctx.reply, "value is not an integer or out of range"); return 0; } m_clients_holder.ChangeCurrentDB(ctx.conn, ctx.currentDB); fill_status_reply(ctx.reply, "OK"); return 0; }
int Ardb::Discard(Context& ctx, RedisCommandFrame& cmd) { if (!ctx.InTransc()) { fill_error_reply(ctx.reply, "DISCARD without MULTI"); return 0; } ctx.ClearTransc(); fill_status_reply(ctx.reply, "OK"); return 0; }
int Ardb::Multi(Context& ctx, RedisCommandFrame& cmd) { if (ctx.InTransc()) { fill_error_reply(ctx.reply, "MULTI calls can not be nested"); return 0; } ctx.GetTransc().in_transc = true; fill_status_reply(ctx.reply, "OK"); return 0; }
/* * GEOADD key MERCATOR|WGS84 x y value [attr_name attr_value ...] */ int ArdbServer::GeoAdd(ArdbConnContext& ctx, RedisCommandFrame& cmd) { GeoAddOptions options; std::string err; if (0 != options.Parse(cmd.GetArguments(), err, 1)) { fill_error_reply(ctx.reply, "%s", err.c_str()); return 0; } int ret = m_db->GeoAdd(ctx.currentDB, cmd.GetArguments()[0], options); CHECK_ARDB_RETURN_VALUE(ctx.reply, ret); if (ret >= 0) { fill_status_reply(ctx.reply, "OK"); } else { fill_error_reply(ctx.reply, "Failed to %s", cmd.ToString().c_str()); } return 0; }
int ArdbServer::PSetEX(ArdbConnContext& ctx, RedisCommandFrame& cmd) { uint32 mills; if (!string_touint32(cmd.GetArguments()[1], mills)) { fill_error_reply(ctx.reply, "value is not an integer or out of range"); return 0; } m_db->PSetEx(ctx.currentDB, cmd.GetArguments()[0], cmd.GetArguments()[2], mills); fill_status_reply(ctx.reply, "OK"); return 0; }
int ArdbServer::SetRange(ArdbConnContext& ctx, RedisCommandFrame& cmd) { int32 offset; if (!string_toint32(cmd.GetArguments()[1], offset)) { fill_error_reply(ctx.reply, "value is not an integer or out of range"); return 0; } int ret = m_db->SetRange(ctx.currentDB, cmd.GetArguments()[0], offset, cmd.GetArguments()[2]); fill_int_reply(ctx.reply, ret); return 0; }
int ArdbServer::GetRange(ArdbConnContext& ctx, RedisCommandFrame& cmd) { int32 start, end; if (!string_toint32(cmd.GetArguments()[1], start) || !string_toint32(cmd.GetArguments()[2], end)) { fill_error_reply(ctx.reply, "value is not an integer or out of range"); return 0; } std::string v; m_db->GetRange(ctx.currentDB, cmd.GetArguments()[0], start, end, v); fill_str_reply(ctx.reply, v); return 0; }
int Ardb::Sort(Context& ctx, RedisCommandFrame& cmd) { DataArray vs; std::string key = cmd.GetArguments()[0]; SortOptions options; if (!options.Parse(cmd.GetArguments(), 1)) { fill_error_reply(ctx.reply, "Invalid SORT command or invalid state for SORT."); return 0; } int ret = SortCommand(ctx, key, options, vs); ctx.reply.Clear(); if (ret < 0) { fill_error_reply(ctx.reply, "Invalid SORT command or invalid state for SORT."); } else { fill_array_reply(ctx.reply, vs); } return 0; }
int Ardb::Exec(Context& ctx, RedisCommandFrame& cmd) { if (!ctx.InTransc()) { fill_error_reply(ctx.reply, "EXEC without MULTI"); return 0; } if (ctx.GetTransc().abort) { ctx.reply.type = REDIS_REPLY_NIL; ctx.ClearTransc(); return 0; } LockGuard<ThreadMutex> guard(g_transc_mutex); //only one transc allowed exec at the same time in multi threads RedisCommandFrameArray::iterator it = ctx.GetTransc().cached_cmds.begin(); Context transc_ctx; transc_ctx.currentDB = ctx.currentDB; while (it != ctx.GetTransc().cached_cmds.end()) { RedisReply& r = ctx.reply.AddMember(); RedisCommandHandlerSetting* setting = FindRedisCommandHandlerSetting(*it); if(NULL != setting) { transc_ctx.reply.Clear(); DoCall(transc_ctx, *setting, *it); r.Clone(transc_ctx.reply); } else { fill_error_reply(r, "unknown command '%s'", it->GetCommand().c_str()); } it++; } ctx.currentDB = transc_ctx.currentDB; ctx.ClearTransc(); UnwatchKeys(ctx); return 0; }
int ArdbServer::Decr(ArdbConnContext& ctx, RedisCommandFrame& cmd) { int64_t val; int ret = m_db->Decr(ctx.currentDB, cmd.GetArguments()[0], val); if (ret == 0) { fill_int_reply(ctx.reply, val); } else { fill_error_reply(ctx.reply, "value is not an integer or out of range"); } return 0; }
int Comms::BLPop(Context& ctx, RedisCommandFrame& cmd) { uint32 timeout; if (!string_touint32(cmd.GetArguments()[cmd.GetArguments().size() - 1], timeout)) { fill_error_reply(ctx.reply, "timeout is not an integer or out of range"); return 0; } bool lpop = cmd.GetType() == REDIS_CMD_BLPOP; for (uint32 i = 0; i < cmd.GetArguments().size() - 1; i++) { std::string v; int err = lpop ? m_kv_store->LPop(ctx.currentDB, cmd.GetArguments()[i], v) : m_kv_store->RPop(ctx.currentDB, cmd.GetArguments()[i], v); if (0 == err && !v.empty()) { RedisReply& r1 = ctx.reply.AddMember(); RedisReply& r2 = ctx.reply.AddMember(); fill_str_reply(r1, cmd.GetArguments()[i]); fill_str_reply(r2, v); FireKeyChangedEvent(ctx, ctx.currentDB, cmd.GetArguments()[i]); RedisCommandFrame list_pop(lpop ? "lpop" : "rpop"); list_pop.AddArg(cmd.GetArguments()[i]); RewriteClientCommand(ctx, list_pop); return 0; } if (err != 0 && err != mmkv::ERR_ENTRY_NOT_EXIST && err != mmkv::ERR_DB_NOT_EXIST) { FillErrorReply(ctx, err); return 0; } } if (NULL != ctx.client) { ctx.client->DetachFD(); ctx.GetBlockContext().lpop = cmd.GetType() == REDIS_CMD_BLPOP; if (timeout > 0) { ctx.block->blocking_timer_task_id = ctx.client->GetService().GetTimer().ScheduleHeapTask( new BlockListTimeout(&ctx), timeout, -1, SECONDS); } } for (uint32 i = 0; i < cmd.GetArguments().size() - 1; i++) { AddBlockKey(ctx, cmd.GetArguments()[i]); } return 0; }
int Ardb::Watch(Context& ctx, RedisCommandFrame& cmd) { if (ctx.InTransc()) { fill_error_reply(ctx.reply, "WATCH inside MULTI is not allowed"); return 0; } for (uint32 i = 0; i < cmd.GetArguments().size(); i++) { WatchForKey(ctx, cmd.GetArguments()[i]); } fill_status_reply(ctx.reply, "OK"); return 0; }
int ArdbServer::Append(ArdbConnContext& ctx, RedisCommandFrame& cmd) { const std::string& key = cmd.GetArguments()[0]; const std::string& value = cmd.GetArguments()[1]; int ret = m_db->Append(ctx.currentDB, key, value); if (ret > 0) { fill_int_reply(ctx.reply, ret); } else { fill_error_reply(ctx.reply, "failed to append key:%s", key.c_str()); } return 0; }
int ArdbServer::GeoSearch(ArdbConnContext& ctx, RedisCommandFrame& cmd) { GeoSearchOptions options; std::string err; if (0 != options.Parse(cmd.GetArguments(), err, 1)) { fill_error_reply(ctx.reply, "%s", err.c_str()); return 0; } ValueDataDeque res; int ret = m_db->GeoSearch(ctx.currentDB, cmd.GetArguments()[0], options, res); CHECK_ARDB_RETURN_VALUE(ctx.reply, ret); fill_array_reply(ctx.reply, res); return 0; }
int Ardb::RenameList(Context& ctx, DBID srcdb, const std::string& srckey, DBID dstdb, const std::string& dstkey) { Context tmpctx; tmpctx.currentDB = srcdb; ValueObject v; int err = GetMetaValue(tmpctx, srckey, LIST_META, v); CHECK_ARDB_RETURN_VALUE(ctx.reply, err); if (0 != err) { fill_error_reply(ctx.reply, "no such key or some error"); return 0; } if (v.meta.Encoding() == COLLECTION_ENCODING_ZIPLIST) { DelKeyValue(tmpctx, v.key); v.key.encode_buf.Clear(); v.key.db = dstdb; v.key.key = dstkey; v.meta.expireat = 0; SetKeyValue(ctx, v); } else { ListIterator iter; ListIter(ctx, v, iter, false); tmpctx.currentDB = dstdb; ValueObject dstmeta; dstmeta.key.type = KEY_META; dstmeta.key.key = dstkey; dstmeta.type = LIST_META; dstmeta.meta.SetFlag(COLLECTION_FLAG_SEQLIST); dstmeta.meta.SetEncoding(COLLECTION_ENCODING_ZIPLIST); BatchWriteGuard guard(GetKeyValueEngine()); while (iter.Valid()) { std::string tmpstr; ListInsert(tmpctx, dstmeta, NULL, iter.Element()->GetDecodeString(tmpstr), false, false); iter.Next(); } SetKeyValue(tmpctx, dstmeta); tmpctx.currentDB = srcdb; DeleteKey(tmpctx, srckey); } ctx.data_change = true; return 0; }
int Ardb::LInsert(Context& ctx, RedisCommandFrame& cmd) { bool head = false; if (!strcasecmp(cmd.GetArguments()[1].c_str(), "before")) { head = true; } else if (!strcasecmp(cmd.GetArguments()[1].c_str(), "after")) { head = false; } else { fill_error_reply(ctx.reply, "Syntax error"); return 0; } return ListInsert(ctx, cmd.GetArguments()[0], &(cmd.GetArguments()[2]), cmd.GetArguments()[3], head, true); }
int ArdbServer::MSet(ArdbConnContext& ctx, RedisCommandFrame& cmd) { if (cmd.GetArguments().size() % 2 != 0) { fill_error_reply(ctx.reply, "wrong number of arguments for MSET"); return 0; } SliceArray keys; SliceArray vals; for (uint32 i = 0; i < cmd.GetArguments().size(); i += 2) { keys.push_back(cmd.GetArguments()[i]); vals.push_back(cmd.GetArguments()[i + 1]); } m_db->MSet(ctx.currentDB, keys, vals); fill_status_reply(ctx.reply, "OK"); return 0; }