Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Compatibility] Added COMMAND GETKEYS and GETKEYSANDFLAGS command #888

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Next Next commit
Added COMMAND GETKEYS and GETKEYSANDFLAGS command
  • Loading branch information
Vijay-Nirmal committed Dec 17, 2024
commit 6460ad68a7763f84b6601eb2104afdc6b3139f29
155 changes: 155 additions & 0 deletions libs/server/Resp/BasicCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
Expand Down Expand Up @@ -1149,6 +1150,160 @@ private bool NetworkCOMMAND_INFO()
return true;
}

/// <summary>
/// Processes COMMAND GETKEYS subcommand.
/// </summary>
private bool NetworkCOMMAND_GETKEYS()
{
if (parseState.Count == 0)
{
return AbortWithWrongNumberOfArguments(nameof(RespCommand.COMMAND_GETKEYS));
}

var cmdName = parseState.GetString(0).ToUpperInvariant();
bool cmdFound = RespCommandsInfo.TryGetRespCommandInfo(cmdName, out var cmdInfo, true, true, logger) ||
storeWrapper.customCommandManager.TryGetCustomCommandInfo(cmdName, out cmdInfo);

if (!cmdFound)
{
return AbortWithErrorMessage(CmdStrings.RESP_INVALID_COMMAND_SPECIFIED);
}

if (cmdInfo.KeySpecifications == null || cmdInfo.KeySpecifications.Length == 0)
{
return AbortWithErrorMessage(CmdStrings.RESP_COMMAND_HAS_NO_KEY_ARGS);
}

var keyList = new List<byte[]>();
foreach (var spec in cmdInfo.KeySpecifications)
{
ExtractKeys(spec, keyList);
}

while (!RespWriteUtils.WriteArrayLength(keyList.Count, ref dcurr, dend))
SendAndReset();

foreach (var key in keyList)
{
while (!RespWriteUtils.WriteBulkString(key, ref dcurr, dend))
SendAndReset();
}

return true;
}

/// <summary>
/// Processes COMMAND GETKEYSANDFLAGS subcommand.
/// </summary>
private bool NetworkCOMMAND_GETKEYSANDFLAGS()
{
if (parseState.Count == 0)
{
return AbortWithWrongNumberOfArguments(nameof(RespCommand.COMMAND_GETKEYSANDFLAGS));
}

var cmdName = parseState.GetString(0).ToUpperInvariant();
bool cmdFound = RespCommandsInfo.TryGetRespCommandInfo(cmdName, out var cmdInfo, true, true, logger) ||
storeWrapper.customCommandManager.TryGetCustomCommandInfo(cmdName, out cmdInfo);

if (!cmdFound)
{
return AbortWithErrorMessage(CmdStrings.RESP_INVALID_COMMAND_SPECIFIED);
}

if (cmdInfo.KeySpecifications == null || cmdInfo.KeySpecifications.Length == 0)
{
return AbortWithErrorMessage(CmdStrings.RESP_COMMAND_HAS_NO_KEY_ARGS);
}

var keyList = new List<byte[]>();
var flagsList = new List<string[]>();

foreach (var spec in cmdInfo.KeySpecifications)
{
var keyCount = keyList.Count;
ExtractKeys(spec, keyList);
var flags = EnumUtils.GetEnumDescriptions(spec.Flags);
for (int i = keyCount; i < keyList.Count; i++)
{
flagsList.Add(flags);
}
}

while (!RespWriteUtils.WriteArrayLength(keyList.Count, ref dcurr, dend))
SendAndReset();

for (int i = 0; i < keyList.Count; i++)
{
while (!RespWriteUtils.WriteArrayLength(2, ref dcurr, dend))
SendAndReset();

while (!RespWriteUtils.WriteBulkString(keyList[i], ref dcurr, dend))
SendAndReset();

while (!RespWriteUtils.WriteArrayLength(flagsList[i].Length, ref dcurr, dend))
SendAndReset();

foreach (var flag in flagsList[i])
{
while (!RespWriteUtils.WriteBulkString(Encoding.ASCII.GetBytes(flag), ref dcurr, dend))
SendAndReset();
}
}

return true;
}

private void ExtractKeys(RespCommandKeySpecification spec, List<byte[]> keyList)
{
int startIndex = 0;

if (spec.BeginSearch is BeginSearchIndex bsIndex)
{
startIndex = bsIndex.Index;
}
else if (spec.BeginSearch is BeginSearchKeyword bsKeyword)
{
for (int i = bsKeyword.StartFrom; i < parseState.Count; i++)
{
if (parseState.GetArgSliceByRef(i).ReadOnlySpan.EqualsUpperCaseSpanIgnoringCase(Encoding.ASCII.GetBytes(bsKeyword.Keyword)))
{
startIndex = i + 1;
break;
}
}
}

if (startIndex >= parseState.Count)
return;

if (spec.FindKeys is FindKeysRange range)
{
var lastKey = range.LastKey == -1 ? parseState.Count - 1 :
Math.Min(startIndex + range.LastKey, parseState.Count - 1);

for (int i = startIndex; i <= lastKey; i += range.KeyStep)
{
if (i < parseState.Count)
{
keyList.Add(parseState.GetArgSliceByRef(i).ToArray());
}
}
}
else if (spec.FindKeys is FindKeysKeyNum keyNum)
{
if (keyNum.KeyNumIdx >= 0 && keyNum.KeyNumIdx < parseState.Count &&
parseState.TryGetInt(startIndex + keyNum.KeyNumIdx, out var count))
{
var firstKey = startIndex + keyNum.FirstKey;
for (int i = 0; i < count && firstKey + i * keyNum.KeyStep < parseState.Count; i++)
{
keyList.Add(parseState.GetArgSliceByRef(firstKey + i * keyNum.KeyStep).ToArray());
}
}
}
}

private bool NetworkECHO()
{
if (parseState.Count != 1)
Expand Down
4 changes: 4 additions & 0 deletions libs/server/Resp/CmdStrings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ static partial class CmdStrings
public static ReadOnlySpan<byte> info => "info"u8;
public static ReadOnlySpan<byte> DOCS => "DOCS"u8;
public static ReadOnlySpan<byte> docs => "docs"u8;
public static ReadOnlySpan<byte> GETKEYS => "GETKEYS"u8;
public static ReadOnlySpan<byte> GETKEYSANDFLAGS => "GETKEYSANDFLAGS"u8;
public static ReadOnlySpan<byte> COMMAND => "COMMAND"u8;
public static ReadOnlySpan<byte> LATENCY => "LATENCY"u8;
public static ReadOnlySpan<byte> CLUSTER => "CLUSTER"u8;
Expand Down Expand Up @@ -212,6 +214,8 @@ static partial class CmdStrings
public static ReadOnlySpan<byte> RESP_ERR_INCR_SUPPORTS_ONLY_SINGLE_PAIR => "ERR INCR option supports a single increment-element pair"u8;
public static ReadOnlySpan<byte> RESP_ERR_INVALID_BITFIELD_TYPE => "ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is"u8;
public static ReadOnlySpan<byte> RESP_ERR_SCRIPT_FLUSH_OPTIONS => "ERR SCRIPT FLUSH only support SYNC|ASYNC option"u8;
public static ReadOnlySpan<byte> RESP_INVALID_COMMAND_SPECIFIED => "Invalid command specified"u8;
public static ReadOnlySpan<byte> RESP_COMMAND_HAS_NO_KEY_ARGS => "The command has no key arguments"u8;

/// <summary>
/// Response string templates
Expand Down
14 changes: 14 additions & 0 deletions libs/server/Resp/Parser/RespCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ public enum RespCommand : ushort
COMMAND_COUNT,
COMMAND_DOCS,
COMMAND_INFO,
COMMAND_GETKEYS,
COMMAND_GETKEYSANDFLAGS,

MEMORY,
// MEMORY_USAGE is a read-only command, so moved up
Expand Down Expand Up @@ -370,6 +372,8 @@ public static class RespCommandExtensions
RespCommand.COMMAND_COUNT,
RespCommand.COMMAND_DOCS,
RespCommand.COMMAND_INFO,
RespCommand.COMMAND_GETKEYS,
RespCommand.COMMAND_GETKEYSANDFLAGS,
RespCommand.MEMORY_USAGE,
// Config
RespCommand.CONFIG_GET,
Expand Down Expand Up @@ -1704,6 +1708,16 @@ private RespCommand SlowParseCommand(ref int count, ref ReadOnlySpan<byte> speci
{
return RespCommand.COMMAND_DOCS;
}

if (subCommand.EqualsUpperCaseSpanIgnoringCase(CmdStrings.GETKEYS))
{
return RespCommand.COMMAND_GETKEYS;
}

if (subCommand.EqualsUpperCaseSpanIgnoringCase(CmdStrings.GETKEYSANDFLAGS))
{
return RespCommand.COMMAND_GETKEYSANDFLAGS;
}
}
else if (command.SequenceEqual(CmdStrings.PING))
{
Expand Down
2 changes: 2 additions & 0 deletions libs/server/Resp/RespServerSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,8 @@ private bool ProcessOtherCommands<TGarnetApi>(RespCommand command, ref TGarnetAp
RespCommand.COMMAND_COUNT => NetworkCOMMAND_COUNT(),
RespCommand.COMMAND_DOCS => NetworkCOMMAND_DOCS(),
RespCommand.COMMAND_INFO => NetworkCOMMAND_INFO(),
RespCommand.COMMAND_GETKEYS => NetworkCOMMAND_GETKEYS(),
RespCommand.COMMAND_GETKEYSANDFLAGS => NetworkCOMMAND_GETKEYSANDFLAGS(),
RespCommand.ECHO => NetworkECHO(),
RespCommand.HELLO => NetworkHELLO(),
RespCommand.TIME => NetworkTIME(),
Expand Down
2 changes: 2 additions & 0 deletions playground/CommandInfoUpdater/SupportedCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ public class SupportedCommand
new("COMMAND|INFO", RespCommand.COMMAND_INFO),
new("COMMAND|COUNT", RespCommand.COMMAND_COUNT),
new("COMMAND|DOCS", RespCommand.COMMAND_DOCS),
new("COMMAND|COMMAND_GETKEYS", RespCommand.COMMAND_GETKEYS),
new("COMMAND|COMMAND_GETKEYSANDFLAGS", RespCommand.COMMAND_GETKEYSANDFLAGS),
]),
new("COMMITAOF", RespCommand.COMMITAOF),
new("CONFIG", RespCommand.CONFIG,
Expand Down