diff --git a/go/api/base_client.go b/go/api/base_client.go index e28c28a4c7..45f37bb50a 100644 --- a/go/api/base_client.go +++ b/go/api/base_client.go @@ -3492,3 +3492,82 @@ func (client *baseClient) XClaimJustIdWithOptions( } return handleStringArrayResponse(result) } + +// Copies the value stored at the source to the destination key if the +// destination key does not yet exist. +// +// Note: +// +// When in cluster mode, both source and destination must map to the same hash slot. +// +// Parameters: +// +// source - The key to the source value. +// destination - The key where the value should be copied to. +// +// Return value: +// +// `true` if source was copied, `false` if source was not copied. +// +// Example: +// +// result, err := client.Copy("source, destination") +// if err != nil { +// // handle error +// } +// fmt.Println(result) // Output: true +// +// [valkey.io]: https://valkey.io/commands/copy/ +func (client *baseClient) Copy(source string, destination string) (bool, error) { + result, err := client.executeCommand(C.Copy, []string{source, destination}) + if err != nil { + return defaultBoolResponse, err + } + return handleBoolResponse(result) +} + +// Copies the value stored at the source to the destination key. When +// replace is true, removes the destination key first if it already +// exists, otherwise performs no action. +// +// Note: +// +// When in cluster mode, both source and destination must map to the same hash slot. +// +// Parameters: +// +// source - The key to the source value. +// destination - The key where the value should be copied to. +// copyOptions - Set copy options with replace and DB destination-db +// +// Return value: +// +// `true` if source was copied, `false` if source was not copied. +// +// Example: +// +// copyOptions := api.NewCopyOptionsBuilder().SetDBDestination(2).SetReplace() +// result, err := client.CopyWithOptions(source, destination",copyOptions) +// if err != nil { +// // handle error +// } +// fmt.Println(result) // Output: true +// +// [valkey.io]: https://valkey.io/commands/copy/ +func (client *baseClient) CopyWithOptions( + source string, + destination string, + options *CopyOptions, +) (bool, error) { + optionArgs, err := options.toArgs() + if err != nil { + return defaultBoolResponse, err + } + result, err := client.executeCommand(C.Copy, append([]string{ + source, destination, + }, optionArgs...)) + if err != nil { + return defaultBoolResponse, err + } + return handleBoolResponse(result) +} diff --git a/go/api/command_options.go b/go/api/command_options.go index dcf17446bc..2165062a84 100644 --- a/go/api/command_options.go +++ b/go/api/command_options.go @@ -355,3 +355,41 @@ func (opts *RestoreOptions) toArgs() ([]string, error) { } return args, err } + +// Optional arguments to Copy(source string, destination string, option *CopyOptions) +// +// [valkey.io]: https://valkey.io/commands/Copy/ +type CopyOptions struct { + // The REPLACE option removes the destination key before copying the value to it. + replace bool + // Option allows specifying an alternative logical database index for the destination key + dbDestination int64 +} + +func NewCopyOptionsBuilder() *CopyOptions { + return &CopyOptions{replace: false} +} + +// Custom setter methods to removes the destination key before copying the value to it. +func (restoreOption *CopyOptions) SetReplace() *CopyOptions { + restoreOption.replace = true + return restoreOption +} + +// Custom setter methods to allows specifying an alternative logical database index for the destination key. +func (copyOption *CopyOptions) SetDBDestination(destinationDB int64) *CopyOptions { + copyOption.dbDestination = destinationDB + return copyOption +} + +func (opts *CopyOptions) toArgs() ([]string, error) { + args := []string{} + var err error + if opts.replace { + args = append(args, string("REPLACE")) + } + if opts.dbDestination >= 0 { + args = append(args, "DB", utils.IntToString(opts.dbDestination)) + } + return args, err +} diff --git a/go/api/generic_base_commands.go b/go/api/generic_base_commands.go index 1454063040..63d4017940 100644 --- a/go/api/generic_base_commands.go +++ b/go/api/generic_base_commands.go @@ -705,4 +705,8 @@ type GenericBaseCommands interface { SortReadOnlyWithOptions(key string, sortOptions *options.SortOptions) ([]Result[string], error) Wait(numberOfReplicas int64, timeout int64) (int64, error) + + Copy(source string, destination string) (bool, error) + + CopyWithOptions(source string, destination string, option *CopyOptions) (bool, error) } diff --git a/go/integTest/shared_commands_test.go b/go/integTest/shared_commands_test.go index 02fc2fc4c2..2c02db34e7 100644 --- a/go/integTest/shared_commands_test.go +++ b/go/integTest/shared_commands_test.go @@ -7210,3 +7210,45 @@ func (suite *GlideTestSuite) TestXClaimFailure() { assert.IsType(suite.T(), &api.RequestError{}, err) }) } + +func (suite *GlideTestSuite) TestCopy() { + suite.runWithDefaultClients(func(client api.BaseClient) { + key := "{key}" + uuid.New().String() + key2 := "{key}" + uuid.New().String() + value := "hello" + t := suite.T() + suite.verifyOK(client.Set(key, value)) + + // Test 1: Check the copy command + resultCopy, err := client.Copy(key, key2) + assert.Nil(t, err) + assert.True(t, resultCopy) + + // Test 2: Check if the value stored at the source is same with destination key. + resultGet, err := client.Get(key2) + assert.Nil(t, err) + assert.Equal(t, value, resultGet.Value()) + }) +} + +func (suite *GlideTestSuite) TestCopyWithOptions() { + suite.runWithDefaultClients(func(client api.BaseClient) { + key := "{key}" + uuid.New().String() + key2 := "{key}" + uuid.New().String() + value := "hello" + t := suite.T() + suite.verifyOK(client.Set(key, value)) + suite.verifyOK(client.Set(key2, "World")) + + // Test 1: Check the copy command with options + optsCopy := api.NewCopyOptionsBuilder().SetReplace() + resultCopy, err := client.CopyWithOptions(key, key2, optsCopy) + assert.Nil(t, err) + assert.True(t, resultCopy) + + // Test 2: Check if the value stored at the source is same with destination key. + resultGet, err := client.Get(key2) + assert.Nil(t, err) + assert.Equal(t, value, resultGet.Value()) + }) +}