Skip to content

Commit

Permalink
feat : add support for bytearray/bitmap to append
Browse files Browse the repository at this point in the history
  • Loading branch information
c-harish committed Nov 14, 2024
1 parent 88e4961 commit d11f548
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 20 deletions.
28 changes: 27 additions & 1 deletion docs/src/content/docs/commands/APPEND.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: APPEND
description: The `APPEND` command in DiceDB is used to either set the value of a key or append a value to an existing key. This command allows for both creating and updating key-value pairs.
---

The `APPEND` command in DiceDB is used to either set the value of a key or append a value to an existing key. This command allows for both creating and updating key-value pairs.
The `APPEND` command in DiceDB is used to either set the value of a key or append a value to an existing key and returns the length of the value stored at the specified key after appending. This command allows for both creating and updating key-value pairs.

## Syntax

Expand Down Expand Up @@ -56,8 +56,34 @@ Appending to key `foo` that contains `bar` with `baz`

```bash
127.0.0.1:7379> SET foo bar
OK
127.0.0.1:7379> APPEND foo baz
(integer) 6
127.0.0.1:7379> GET foo
"barbaz"
```

Appending "1" to key `bmkey` that contains a bitmap equivalent of `42`

```bash
127.0.0.1:7379> SETBIT bmkey 2 1
(integer) 0
127.0.0.1:7379> SETBIT bmkey 3 1
(integer) 0
127.0.0.1:7379> SETBIT bmkey 5 1
(integer) 0
127.0.0.1:7379> SETBIT bmkey 10 1
(integer) 0
127.0.0.1:7379> SETBIT bmkey 11 1
(integer) 0
127.0.0.1:7379> SETBIT bmkey 14 1
(integer) 0
127.0.0.1:7379> GET bmkey
"42"
127.0.0.1:7379> APPEND bmkey 1
(integer) 3
127.0.0.1:7379> GET bmkey
"421"
```

### Invalid usage
Expand Down
17 changes: 17 additions & 0 deletions integration_tests/commands/http/append_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,23 @@ func TestAPPEND(t *testing.T) {
{Command: "del", Body: map[string]interface{}{"key": "myzset"}},
},
},
{
name: "APPEND to key created using SETBIT",
commands: []HTTPCommand{
{Command: "SETBIT", Body: map[string]interface{}{"key": "bitkey", "values": []string{"2", "1"}}},
{Command: "SETBIT", Body: map[string]interface{}{"key": "bitkey", "values": []string{"3", "1"}}},
{Command: "SETBIT", Body: map[string]interface{}{"key": "bitkey", "values": []string{"5", "1"}}},
{Command: "SETBIT", Body: map[string]interface{}{"key": "bitkey", "values": []string{"10", "1"}}},
{Command: "SETBIT", Body: map[string]interface{}{"key": "bitkey", "values": []string{"11", "1"}}},
{Command: "SETBIT", Body: map[string]interface{}{"key": "bitkey", "values": []string{"14", "1"}}},
{Command: "APPEND", Body: map[string]interface{}{"key": "bitkey", "value": "1"}},
{Command: "GET", Body: map[string]interface{}{"key": "bitkey"}},
},
expected: []interface{}{float64(0), float64(0), float64(0), float64(0), float64(0), float64(0), float64(3), "421"},
cleanup: []HTTPCommand{
{Command: "del", Body: map[string]interface{}{"key": "bitkey"}},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
Expand Down
6 changes: 6 additions & 0 deletions integration_tests/commands/resp/append_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ func TestAPPEND(t *testing.T) {
expected: []interface{}{int64(1), "WRONGTYPE Operation against a key holding the wrong kind of value"},
cleanup: []string{"del key"},
},
{
name: "APPEND to key created using SETBIT",
commands: []string{"SETBIT bitkey 2 1", "SETBIT bitkey 3 1", "SETBIT bitkey 5 1", "SETBIT bitkey 10 1", "SETBIT bitkey 11 1", "SETBIT bitkey 14 1", "APPEND bitkey 1", "GET bitkey"},
expected: []interface{}{int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), int64(3), "421"},
cleanup: []string{"del bitkey"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
Expand Down
6 changes: 6 additions & 0 deletions integration_tests/commands/websocket/append_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ func TestAppend(t *testing.T) {
expected: []interface{}{float64(1), "WRONGTYPE Operation against a key holding the wrong kind of value"},
cleanupKey: "key",
},
{
name: "APPEND to key created using SETBIT",
commands: []string{"SETBIT bitkey 2 1", "SETBIT bitkey 3 1", "SETBIT bitkey 5 1", "SETBIT bitkey 10 1", "SETBIT bitkey 11 1", "SETBIT bitkey 14 1", "APPEND bitkey 1", "GET bitkey"},
expected: []interface{}{float64(0), float64(0), float64(0), float64(0), float64(0), float64(0), float64(3), "421"},
cleanupKey: "bitkey",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
Expand Down
15 changes: 10 additions & 5 deletions internal/eval/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7031,17 +7031,22 @@ func testEvalAPPEND(t *testing.T, store *dstore.Store) {
input: []string{"hashKey", "val"},
migratedOutput: EvalResponse{Result: nil, Error: diceerrors.ErrWrongTypeOperation},
},
"append to key created using SETBIT": {
"append to key containing byte array": {
setup: func() {
key := "bitKey"
// Create a new byte array object
initialByteArray := NewByteArray(1) // Initialize with 1 byte
initialByteArray.SetBit(0, true) // Set the first bit to 1
initialByteArray := NewByteArray(2) // Initialize with 2 byte
initialByteArray.SetBit(2, true) // Set the third bit to 1
initialByteArray.SetBit(3, true) // Set the fourth bit to 1
initialByteArray.SetBit(5, true) // Set the sixth bit to 1
initialByteArray.SetBit(10, true) // Set the eleventh bit to 1
initialByteArray.SetBit(11, true) // Set the twelfth bit to 1
initialByteArray.SetBit(14, true) // Set the fifteenth bit to 1
obj := store.NewObj(initialByteArray, -1, object.ObjTypeByteArray, object.ObjEncodingByteArray)
store.Put(key, obj)
},
input: []string{"bitKey", "val"},
migratedOutput: EvalResponse{Result: nil, Error: diceerrors.ErrWrongTypeOperation},
input: []string{"bitKey", "1"},
migratedOutput: EvalResponse{Result: 3, Error: nil},
},
"append value with leading zeros": {
setup: func() {
Expand Down
34 changes: 20 additions & 14 deletions internal/eval/store_eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -1115,25 +1115,33 @@ func evalAPPEND(args []string, store *dstore.Store) *EvalResponse {
Error: nil,
}
}
// Key exists path
if _, ok := obj.Value.(*sortedset.Set); ok {
return &EvalResponse{
Result: nil,
Error: diceerrors.ErrWrongTypeOperation,
}
}
_, currentEnc := object.ExtractTypeEncoding(obj)

var currentValueStr string
switch currentEnc {
switch currentType, currentEnc := object.ExtractTypeEncoding(obj); currentEnc {
case object.ObjEncodingInt:
// If the encoding is an integer, convert the current value to a string for concatenation
currentValueStr = strconv.FormatInt(obj.Value.(int64), 10)
case object.ObjEncodingEmbStr, object.ObjEncodingRaw:
// If the encoding is a string, retrieve the string value for concatenation
currentValueStr = obj.Value.(string)
// If the encoding and type is a string, retrieve the string value for concatenation
if currentType == object.ObjTypeString {
currentValueStr = obj.Value.(string)
} else {
return &EvalResponse{
Result: nil,
Error: diceerrors.ErrWrongTypeOperation,
}
}
case object.ObjEncodingByteArray:
if val, ok := obj.Value.(*ByteArray); ok {
currentValueStr = string(val.data)
} else {
return &EvalResponse{
Result: nil,
Error: diceerrors.ErrWrongTypeOperation,
}
}
default:
// If the encoding is neither integer nor string, return a "wrong type" error
// If the encoding is neither integer, string, nor byte array, return a "wrong type" error
return &EvalResponse{
Result: nil,
Error: diceerrors.ErrWrongTypeOperation,
Expand Down Expand Up @@ -1401,7 +1409,6 @@ func jsonGETHelper(store *dstore.Store, path, key string) *EvalResponse {
// If path is root, return the entire JSON
if path == defaultRootPath {
resultBytes, err := sonic.Marshal(jsonData)
fmt.Println(string(resultBytes))
if err != nil {
return &EvalResponse{
Result: nil,
Expand Down Expand Up @@ -5212,7 +5219,6 @@ func evalSETBIT(args []string, store *dstore.Store) *EvalResponse {
// resize as per the offset
byteArray = byteArray.IncreaseSize(int(requiredByteArraySize))
}

resp := byteArray.GetBit(int(offset))
byteArray.SetBit(int(offset), value)

Expand Down

0 comments on commit d11f548

Please sign in to comment.