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

PR: Support for OBJECT ENCODING #977

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 73 additions & 2 deletions integration_tests/commands/async/object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import (
func TestObjectCommand(t *testing.T) {
conn := getLocalConnection()
defer conn.Close()
simpleJSON := `{"name":"John","age":30}`

testCases := []struct {
name string
commands []string
name string
commands []string
expected []interface{}
assertType []string
delay []time.Duration
Expand All @@ -25,6 +26,76 @@ func TestObjectCommand(t *testing.T) {
assertType: []string{"equal", "assert", "assert", "equal", "assert"},
delay: []time.Duration{0, 2 * time.Second, 3 * time.Second, 0, 0},
},
{
name: "Object Encoding check for raw",
commands: []string{"SET foo foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar", "OBJECT ENCODING foo"},
expected: []interface{}{"OK", "raw"},
assertType: []string{"equal", "equal"},
delay: []time.Duration{0, 2 * time.Second},
},
{
name: "Object Encoding check for int",
commands: []string{"SET foo 1", "OBJECT ENCODING foo"},
expected: []interface{}{"OK", "int"},
assertType: []string{"equal", "equal"},
delay: []time.Duration{0, 2 * time.Second},
},
{
name: "Object Encoding check for embstr",
commands: []string{"SET foo bar", "OBJECT ENCODING foo"},
expected: []interface{}{"OK", "embstr"},
assertType: []string{"equal", "equal"},
delay: []time.Duration{0, 2 * time.Second},
},
{
name: "Object Encoding check for deque",
commands: []string{"LPUSH listKey 'value1'", "LPUSH listKey 'value2'", "OBJECT ENCODING listKey"},
expected: []interface{}{"OK", "OK", "deque"},
assertType: []string{"equal", "equal", "equal"},
delay: []time.Duration{0, 2 * time.Second, 3 * time.Second},
},
{
name: "Object Encoding check for bf",
commands: []string{"BFADD bloomkey value1", "BFADD bloomkey value2", "OBJECT ENCODING bloomkey"},
expected: []interface{}{int64(1), int64(1), "bf"},
assertType: []string{"assert", "assert", "equal"},
delay: []time.Duration{0, 2 * time.Second, 3 * time.Second},
},
{
name: "Object Encoding check for json",
commands: []string{`JSON.SET k1 $ ` + simpleJSON, "OBJECT ENCODING k1"},
expected: []interface{}{"OK", "json"},
assertType: []string{"equal", "equal"},
delay: []time.Duration{0, 2 * time.Second},
},
{
name: "Object Encoding check for bytearray",
commands: []string{"SETBIT kbitset 0 1", "SETBIT kbitset 1 0", "SETBIT kbitset 2 1", "OBJECT ENCODING kbitset"},
expected: []interface{}{int64(0), int64(0), int64(0), "bytearray"},
assertType: []string{"assert", "assert", "assert", "equal"},
delay: []time.Duration{0, 2 * time.Second, 3 * time.Second, 4 * time.Second},
},
{
name: "Object Encoding check for hashmap",
commands: []string{"HSET hashKey hKey hValue", "OBJECT ENCODING hashKey"},
expected: []interface{}{int64(1), "hashmap"},
assertType: []string{"assert", "equal"},
delay: []time.Duration{0, 2 * time.Second},
},
{
name: "Object Encoding check for btree",
commands: []string{"ZADD btreekey 1 'member1' 2 'member2'", "OBJECT ENCODING btreekey"},
expected: []interface{}{int64(2), "btree"},
assertType: []string{"equal", "equal"},
delay: []time.Duration{0, 2 * time.Second},
},
{
name: "Object Encoding check for setstr",
commands: []string{"SADD skey one two three", "OBJECT ENCODING skey"},
expected: []interface{}{int64(3), "setstr"},
assertType: []string{"assert", "equal"},
delay: []time.Duration{0, 2 * time.Second},
},
}

for _, tc := range testCases {
Expand Down
70 changes: 70 additions & 0 deletions internal/eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -3344,6 +3344,73 @@ func evalObjectIdleTime(key string, store *dstore.Store) []byte {
return clientio.Encode(int64(dstore.GetIdleTime(obj.LastAccessedAt)), true)
}

// OBJECT COMMAND returns the internal encoding of key stored in dicedb
func evalObjectEncoding(key string, store *dstore.Store) []byte {
var encodingTypeStr string

obj := store.GetNoTouch(key)
if obj == nil {
return clientio.RespNIL
}

if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeString, object.ObjEncodingRaw); err == nil {
encodingTypeStr = "raw"
return clientio.Encode(encodingTypeStr, false)
}

if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeString, object.ObjEncodingEmbStr); err == nil {
encodingTypeStr = "embstr"
return clientio.Encode(encodingTypeStr, false)
}

if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeInt, object.ObjEncodingInt); err == nil {
encodingTypeStr = "int"
return clientio.Encode(encodingTypeStr, false)
}

if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeByteList, object.ObjEncodingDeque); err == nil {
encodingTypeStr = "deque"
return clientio.Encode(encodingTypeStr, false)
}

if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeBitSet, object.ObjEncodingBF); err == nil {
encodingTypeStr = "bf"
return clientio.Encode(encodingTypeStr, false)
}

if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeJSON, object.ObjEncodingJSON); err == nil {
encodingTypeStr = "json"
return clientio.Encode(encodingTypeStr, false)
}

if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeByteArray, object.ObjEncodingByteArray); err == nil {
encodingTypeStr = "bytearray"
return clientio.Encode(encodingTypeStr, false)
}

if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeSet, object.ObjEncodingSetStr); err == nil {
encodingTypeStr = "setstr"
return clientio.Encode(encodingTypeStr, false)
}

if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeSet, object.ObjEncodingSetInt); err == nil {
encodingTypeStr = "setint"
return clientio.Encode(encodingTypeStr, false)
}

if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err == nil {
encodingTypeStr = "hashmap"
return clientio.Encode(encodingTypeStr, false)
}

if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeSortedSet, object.ObjEncodingBTree); err == nil {
encodingTypeStr = "btree"
return clientio.Encode(encodingTypeStr, false)
}

return diceerrors.NewErrWithFormattedMessage(diceerrors.WrongTypeErr)
}

func evalOBJECT(args []string, store *dstore.Store) []byte {
if len(args) < 2 {
return diceerrors.NewErrArity("OBJECT")
Expand All @@ -3355,6 +3422,9 @@ func evalOBJECT(args []string, store *dstore.Store) []byte {
switch subcommand {
case "IDLETIME":
return evalObjectIdleTime(key, store)

case "ENCODING":
return evalObjectEncoding(key, store)
default:
return diceerrors.NewErrWithMessage(diceerrors.SyntaxErr)
}
Expand Down
34 changes: 34 additions & 0 deletions internal/eval/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ func TestEval(t *testing.T) {
testEvalGEOADD(t, store)
testEvalGEODIST(t, store)
testEvalSINTER(t, store)
testEvalObjectEncoding(t, store)
}

func testEvalPING(t *testing.T, store *dstore.Store) {
Expand Down Expand Up @@ -5507,3 +5508,36 @@ func testEvalSINTER(t *testing.T, store *dstore.Store) {

runEvalTests(t, tests, evalSINTER, store)
}
func testEvalObjectEncoding(t *testing.T, store *dstore.Store) {
tests := map[string]evalTestCase{
"nil value": {
setup: func() {},
input: nil,
output: []byte("-ERR wrong number of arguments for 'object' command\r\n"),
},
"empty array": {
setup: func() {},
input: []string{},
output: []byte("-ERR wrong number of arguments for 'object' command\r\n"),
},
"object with invalid subcommand": {
setup: func() {},
input: []string{"TESTSUBCOMMAND", "key"},
output: []byte("-ERR syntax error\r\n"),
},
"key does not exist": {
setup: func() {},
input: []string{"ENCODING", "NONEXISTENT_KEY"},
output: clientio.RespNIL,
},
"key exists": {
setup: func() {
evalLPUSH([]string{"EXISTING_KEY", "mock_value"}, store)
},
input: []string{"ENCODING", "EXISTING_KEY"},
output: []byte("$5\r\ndeque\r\n"),
},
}

runEvalTests(t, tests, evalOBJECT, store)
}