Skip to content

api: fix splice update operation #353

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

Merged
merged 2 commits into from
Nov 17, 2023
Merged
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
constants for `protocol` (#337)
- Change `crud` operations `Timeout` option type to `crud.OptFloat64`
instead of `crud.OptUint` (#342)
- Change all `Upsert` and `Update` requests to accept `*tarantool.Operations`
as `ops` parameters instead of `interface{}` (#348)

### Deprecated

Expand Down Expand Up @@ -80,6 +82,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
`prefer_replica`, `balance`) setup for crud.GetRequest (#335)
- Tests with crud 1.4.0 (#336)
- Tests with case sensitive SQL (#341)
- Splice update operation accepts 3 arguments instead of 5 (#348)

## [1.12.0] - 2023-06-07

Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,17 @@ of the requests is an array instead of array of arrays.

IPROTO constants have been moved to a separate package [go-iproto](https://github.com/tarantool/go-iproto).

#### Request interface
#### Request changes

* The method `Code() uint32` replaced by the `Type() iproto.Type`.
* `Op` struct for update operations made private.
* Removed `OpSplice` struct.
* `Operations.Splice` method now accepts 5 arguments instead of 3.
* Requests `Update`, `UpdateAsync`, `UpdateTyped`, `Upsert`, `UpsertAsync` no
longer accept `ops` argument (operations) as an `interface{}`. `*Operations`
needs to be passed instead.
* `UpdateRequest` and `UpsertRequest` structs no longer accept `interface{}`
for an `ops` field. `*Operations` needs to be used instead.

#### Connect function

Expand Down
77 changes: 47 additions & 30 deletions client_tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,43 @@ func (k IntIntKey) EncodeMsgpack(enc *msgpack.Encoder) error {
return nil
}

// Op - is update operation.
type Op struct {
// operation - is update operation.
type operation struct {
Op string
Field int
Arg interface{}
// Pos, Len, Replace fields used in the Splice operation.
Pos int
Len int
Replace string
}

func (o Op) EncodeMsgpack(enc *msgpack.Encoder) error {
enc.EncodeArrayLen(3)
enc.EncodeString(o.Op)
enc.EncodeInt(int64(o.Field))
func (o operation) EncodeMsgpack(enc *msgpack.Encoder) error {
isSpliceOperation := o.Op == spliceOperator
argsLen := 3
if isSpliceOperation {
argsLen = 5
}
if err := enc.EncodeArrayLen(argsLen); err != nil {
return err
}
if err := enc.EncodeString(o.Op); err != nil {
return err
}
if err := enc.EncodeInt(int64(o.Field)); err != nil {
return err
}

if isSpliceOperation {
if err := enc.EncodeInt(int64(o.Pos)); err != nil {
return err
}
if err := enc.EncodeInt(int64(o.Len)); err != nil {
return err
}
return enc.EncodeString(o.Replace)
}

return enc.Encode(o.Arg)
}

Expand All @@ -82,17 +108,26 @@ const (

// Operations is a collection of update operations.
type Operations struct {
ops []Op
ops []operation
}

// EncodeMsgpack encodes Operations as an array of operations.
func (ops *Operations) EncodeMsgpack(enc *msgpack.Encoder) error {
return enc.Encode(ops.ops)
}

// NewOperations returns a new empty collection of update operations.
func NewOperations() *Operations {
ops := new(Operations)
return ops
return &Operations{[]operation{}}
}

func (ops *Operations) append(op string, field int, arg interface{}) *Operations {
ops.ops = append(ops.ops, Op{op, field, arg})
ops.ops = append(ops.ops, operation{Op: op, Field: field, Arg: arg})
return ops
}

func (ops *Operations) appendSplice(op string, field, pos, len int, replace string) *Operations {
ops.ops = append(ops.ops, operation{Op: op, Field: field, Pos: pos, Len: len, Replace: replace})
return ops
}

Expand Down Expand Up @@ -122,8 +157,8 @@ func (ops *Operations) BitwiseXor(field int, arg interface{}) *Operations {
}

// Splice adds a splice operation to the collection of update operations.
func (ops *Operations) Splice(field int, arg interface{}) *Operations {
return ops.append(spliceOperator, field, arg)
func (ops *Operations) Splice(field, pos, len int, replace string) *Operations {
return ops.appendSplice(spliceOperator, field, pos, len, replace)
}

// Insert adds an insert operation to the collection of update operations.
Expand All @@ -140,21 +175,3 @@ func (ops *Operations) Delete(field int, arg interface{}) *Operations {
func (ops *Operations) Assign(field int, arg interface{}) *Operations {
return ops.append(assignOperator, field, arg)
}

type OpSplice struct {
Op string
Field int
Pos int
Len int
Replace string
}

func (o OpSplice) EncodeMsgpack(enc *msgpack.Encoder) error {
enc.EncodeArrayLen(5)
enc.EncodeString(o.Op)
enc.EncodeInt(int64(o.Field))
enc.EncodeInt(int64(o.Pos))
enc.EncodeInt(int64(o.Len))
enc.EncodeString(o.Replace)
return nil
}
51 changes: 51 additions & 0 deletions client_tools_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package tarantool_test

import (
"bytes"
"testing"

"github.com/vmihailenco/msgpack/v5"

"github.com/tarantool/go-tarantool/v2"
)

func TestOperations_EncodeMsgpack(t *testing.T) {
ops := tarantool.NewOperations().
Add(1, 2).
Subtract(1, 2).
BitwiseAnd(1, 2).
BitwiseOr(1, 2).
BitwiseXor(1, 2).
Splice(1, 2, 3, "a").
Insert(1, 2).
Delete(1, 2).
Assign(1, 2)
refOps := []interface{}{
[]interface{}{"+", 1, 2},
[]interface{}{"-", 1, 2},
[]interface{}{"&", 1, 2},
[]interface{}{"|", 1, 2},
[]interface{}{"^", 1, 2},
[]interface{}{":", 1, 2, 3, "a"},
[]interface{}{"!", 1, 2},
[]interface{}{"#", 1, 2},
[]interface{}{"=", 1, 2},
}

var refBuf bytes.Buffer
encRef := msgpack.NewEncoder(&refBuf)
if err := encRef.Encode(refOps); err != nil {
t.Errorf("error while encoding: %v", err.Error())
}

var buf bytes.Buffer
enc := msgpack.NewEncoder(&buf)

if err := enc.Encode(ops); err != nil {
t.Errorf("error while encoding: %v", err.Error())
}
if !bytes.Equal(refBuf.Bytes(), buf.Bytes()) {
t.Errorf("encode response is wrong:\n expected %v\n got: %v",
refBuf, buf.Bytes())
}
}
11 changes: 6 additions & 5 deletions connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ type Connector interface {
Delete(space, index interface{}, key interface{}) (*Response, error)
// Deprecated: the method will be removed in the next major version,
// use a UpdateRequest object + Do() instead.
Update(space, index interface{}, key, ops interface{}) (*Response, error)
Update(space, index interface{}, key interface{}, ops *Operations) (*Response, error)
// Deprecated: the method will be removed in the next major version,
// use a UpsertRequest object + Do() instead.
Upsert(space interface{}, tuple, ops interface{}) (*Response, error)
Upsert(space interface{}, tuple interface{}, ops *Operations) (*Response, error)
// Deprecated: the method will be removed in the next major version,
// use a CallRequest object + Do() instead.
Call(functionName string, args interface{}) (*Response, error)
Expand Down Expand Up @@ -67,7 +67,8 @@ type Connector interface {
DeleteTyped(space, index interface{}, key interface{}, result interface{}) error
// Deprecated: the method will be removed in the next major version,
// use a UpdateRequest object + Do() instead.
UpdateTyped(space, index interface{}, key, ops interface{}, result interface{}) error
UpdateTyped(space, index interface{}, key interface{}, ops *Operations,
result interface{}) error
// Deprecated: the method will be removed in the next major version,
// use a CallRequest object + Do() instead.
CallTyped(functionName string, args interface{}, result interface{}) error
Expand Down Expand Up @@ -100,10 +101,10 @@ type Connector interface {
DeleteAsync(space, index interface{}, key interface{}) *Future
// Deprecated: the method will be removed in the next major version,
// use a UpdateRequest object + Do() instead.
UpdateAsync(space, index interface{}, key, ops interface{}) *Future
UpdateAsync(space, index interface{}, key interface{}, ops *Operations) *Future
// Deprecated: the method will be removed in the next major version,
// use a UpsertRequest object + Do() instead.
UpsertAsync(space interface{}, tuple interface{}, ops interface{}) *Future
UpsertAsync(space interface{}, tuple interface{}, ops *Operations) *Future
// Deprecated: the method will be removed in the next major version,
// use a CallRequest object + Do() instead.
CallAsync(functionName string, args interface{}) *Future
Expand Down
43 changes: 40 additions & 3 deletions crud/operations.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package crud

import (
"github.com/vmihailenco/msgpack/v5"
)

const (
// Add - operator for addition.
Add Operator = "+"
Expand All @@ -23,11 +27,44 @@ const (

// Operation describes CRUD operation as a table
// {operator, field_identifier, value}.
// Splice operation described as a table
// {operator, field_identifier, position, length, replace_string}.
type Operation struct {
// Instruct msgpack to pack this struct as array, so no custom packer
// is needed.
_msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
Operator Operator
Field interface{} // Number or string.
Value interface{}
// Pos, Len, Replace fields used in the Splice operation.
Pos int
Len int
Replace string
}

// EncodeMsgpack encodes Operation.
func (o Operation) EncodeMsgpack(enc *msgpack.Encoder) error {
isSpliceOperation := o.Operator == Splice
argsLen := 3
if isSpliceOperation {
argsLen = 5
}
if err := enc.EncodeArrayLen(argsLen); err != nil {
return err
}
if err := enc.EncodeString(string(o.Operator)); err != nil {
return err
}
if err := enc.Encode(o.Field); err != nil {
return err
}

if isSpliceOperation {
if err := enc.EncodeInt(int64(o.Pos)); err != nil {
return err
}
if err := enc.EncodeInt(int64(o.Len)); err != nil {
return err
}
return enc.EncodeString(o.Replace)
}

return enc.Encode(o.Value)
}
Loading