Skip to content

Commit a5de8f3

Browse files
DerekBumoleg-jukovec
authored andcommitted
api: fix splice update operation
Splice update operation (`:`) accepts 5 and only 5 arguments. It was parsed and encoded incorrectly with only 3 argument (as every other update operations). Also fixed the same operation for the crud. Part of #348
1 parent 8d4fedd commit a5de8f3

File tree

8 files changed

+245
-54
lines changed

8 files changed

+245
-54
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
8080
`prefer_replica`, `balance`) setup for crud.GetRequest (#335)
8181
- Tests with crud 1.4.0 (#336)
8282
- Tests with case sensitive SQL (#341)
83+
- Splice update operation accepts 3 arguments instead of 5 (#348)
8384

8485
## [1.12.0] - 2023-06-07
8586

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@ interface to get information if the usage of space and index names in requests
253253
is supported.
254254
* `Schema` structure no longer implements `SchemaResolver` interface.
255255

256+
#### Client tools changes
257+
258+
* Remove `OpSplice` struct.
259+
* `Operations.Splice` method now accepts 5 arguments instead of 3.
260+
256261
## Contributing
257262

258263
See [the contributing guide](CONTRIBUTING.md) for detailed instructions on how

client_tools.go

+37-24
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,38 @@ type Op struct {
5959
Op string
6060
Field int
6161
Arg interface{}
62+
// Pos, Len, Replace fields used in the Splice operation.
63+
Pos int
64+
Len int
65+
Replace string
6266
}
6367

6468
func (o Op) EncodeMsgpack(enc *msgpack.Encoder) error {
65-
enc.EncodeArrayLen(3)
66-
enc.EncodeString(o.Op)
67-
enc.EncodeInt(int64(o.Field))
69+
isSpliceOperation := o.Op == spliceOperator
70+
argsLen := 3
71+
if isSpliceOperation {
72+
argsLen = 5
73+
}
74+
if err := enc.EncodeArrayLen(argsLen); err != nil {
75+
return err
76+
}
77+
if err := enc.EncodeString(o.Op); err != nil {
78+
return err
79+
}
80+
if err := enc.EncodeInt(int64(o.Field)); err != nil {
81+
return err
82+
}
83+
84+
if isSpliceOperation {
85+
if err := enc.EncodeInt(int64(o.Pos)); err != nil {
86+
return err
87+
}
88+
if err := enc.EncodeInt(int64(o.Len)); err != nil {
89+
return err
90+
}
91+
return enc.EncodeString(o.Replace)
92+
}
93+
6894
return enc.Encode(o.Arg)
6995
}
7096

@@ -92,7 +118,12 @@ func NewOperations() *Operations {
92118
}
93119

94120
func (ops *Operations) append(op string, field int, arg interface{}) *Operations {
95-
ops.ops = append(ops.ops, Op{op, field, arg})
121+
ops.ops = append(ops.ops, Op{Op: op, Field: field, Arg: arg})
122+
return ops
123+
}
124+
125+
func (ops *Operations) appendSplice(op string, field, pos, len int, replace string) *Operations {
126+
ops.ops = append(ops.ops, Op{Op: op, Field: field, Pos: pos, Len: len, Replace: replace})
96127
return ops
97128
}
98129

@@ -122,8 +153,8 @@ func (ops *Operations) BitwiseXor(field int, arg interface{}) *Operations {
122153
}
123154

124155
// Splice adds a splice operation to the collection of update operations.
125-
func (ops *Operations) Splice(field int, arg interface{}) *Operations {
126-
return ops.append(spliceOperator, field, arg)
156+
func (ops *Operations) Splice(field, pos, len int, replace string) *Operations {
157+
return ops.appendSplice(spliceOperator, field, pos, len, replace)
127158
}
128159

129160
// Insert adds an insert operation to the collection of update operations.
@@ -140,21 +171,3 @@ func (ops *Operations) Delete(field int, arg interface{}) *Operations {
140171
func (ops *Operations) Assign(field int, arg interface{}) *Operations {
141172
return ops.append(assignOperator, field, arg)
142173
}
143-
144-
type OpSplice struct {
145-
Op string
146-
Field int
147-
Pos int
148-
Len int
149-
Replace string
150-
}
151-
152-
func (o OpSplice) EncodeMsgpack(enc *msgpack.Encoder) error {
153-
enc.EncodeArrayLen(5)
154-
enc.EncodeString(o.Op)
155-
enc.EncodeInt(int64(o.Field))
156-
enc.EncodeInt(int64(o.Pos))
157-
enc.EncodeInt(int64(o.Len))
158-
enc.EncodeString(o.Replace)
159-
return nil
160-
}

crud/operations.go

+39-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package crud
22

3+
import (
4+
"github.com/vmihailenco/msgpack/v5"
5+
)
6+
37
const (
48
// Add - operator for addition.
59
Add Operator = "+"
@@ -23,11 +27,43 @@ const (
2327

2428
// Operation describes CRUD operation as a table
2529
// {operator, field_identifier, value}.
30+
// Splice operation described as a table
31+
// {operator, field_identifier, position, length, replace_string}.
2632
type Operation struct {
27-
// Instruct msgpack to pack this struct as array, so no custom packer
28-
// is needed.
29-
_msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
3033
Operator Operator
3134
Field interface{} // Number or string.
3235
Value interface{}
36+
// Pos, Len, Replace fields used in the Splice operation.
37+
Pos int
38+
Len int
39+
Replace string
40+
}
41+
42+
func (o Operation) EncodeMsgpack(enc *msgpack.Encoder) error {
43+
isSpliceOperation := o.Operator == Splice
44+
argsLen := 3
45+
if isSpliceOperation {
46+
argsLen = 5
47+
}
48+
if err := enc.EncodeArrayLen(argsLen); err != nil {
49+
return err
50+
}
51+
if err := enc.EncodeString(string(o.Operator)); err != nil {
52+
return err
53+
}
54+
if err := enc.Encode(o.Field); err != nil {
55+
return err
56+
}
57+
58+
if isSpliceOperation {
59+
if err := enc.EncodeInt(int64(o.Pos)); err != nil {
60+
return err
61+
}
62+
if err := enc.EncodeInt(int64(o.Len)); err != nil {
63+
return err
64+
}
65+
return enc.EncodeString(o.Replace)
66+
}
67+
68+
return enc.Encode(o.Value)
3369
}

crud/tarantool_test.go

+132
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,63 @@ var startOpts test_helpers.StartOpts = test_helpers.StartOpts{
4040
var timeout = float64(1.1)
4141

4242
var operations = []crud.Operation{
43+
// Insert new fields,
44+
// because double update of the same field results in an error.
45+
{
46+
Operator: crud.Insert,
47+
Field: 4,
48+
Value: 0,
49+
},
50+
{
51+
Operator: crud.Insert,
52+
Field: 5,
53+
Value: 0,
54+
},
55+
{
56+
Operator: crud.Insert,
57+
Field: 6,
58+
Value: 0,
59+
},
60+
{
61+
Operator: crud.Insert,
62+
Field: 7,
63+
Value: 0,
64+
},
65+
{
66+
Operator: crud.Insert,
67+
Field: 8,
68+
Value: 0,
69+
},
70+
{
71+
Operator: crud.Add,
72+
Field: 4,
73+
Value: 1,
74+
},
75+
{
76+
Operator: crud.Sub,
77+
Field: 5,
78+
Value: 1,
79+
},
80+
{
81+
Operator: crud.And,
82+
Field: 6,
83+
Value: 1,
84+
},
85+
{
86+
Operator: crud.Or,
87+
Field: 7,
88+
Value: 1,
89+
},
90+
{
91+
Operator: crud.Xor,
92+
Field: 8,
93+
Value: 1,
94+
},
95+
{
96+
Operator: crud.Delete,
97+
Field: 4,
98+
Value: 5,
99+
},
43100
{
44101
Operator: crud.Assign,
45102
Field: "name",
@@ -542,6 +599,81 @@ func TestCrudProcessData(t *testing.T) {
542599
}
543600
}
544601

602+
func TestCrudUpdateSplice(t *testing.T) {
603+
test_helpers.SkipIfCrudSpliceBroken(t)
604+
605+
conn := connect(t)
606+
defer conn.Close()
607+
608+
req := crud.MakeUpdateRequest(spaceName).
609+
Key(key).
610+
Operations([]crud.Operation{
611+
{
612+
Operator: crud.Splice,
613+
Field: "name",
614+
Pos: 1,
615+
Len: 1,
616+
Replace: "!!",
617+
},
618+
}).
619+
Opts(simpleOperationOpts)
620+
621+
testCrudRequestPrepareData(t, conn)
622+
resp, err := conn.Do(req).Get()
623+
testCrudRequestCheck(t, req, resp,
624+
err, 2)
625+
}
626+
627+
func TestCrudUpsertSplice(t *testing.T) {
628+
test_helpers.SkipIfCrudSpliceBroken(t)
629+
630+
conn := connect(t)
631+
defer conn.Close()
632+
633+
req := crud.MakeUpsertRequest(spaceName).
634+
Tuple(tuple).
635+
Operations([]crud.Operation{
636+
{
637+
Operator: crud.Splice,
638+
Field: "name",
639+
Pos: 1,
640+
Len: 1,
641+
Replace: "!!",
642+
},
643+
}).
644+
Opts(simpleOperationOpts)
645+
646+
testCrudRequestPrepareData(t, conn)
647+
resp, err := conn.Do(req).Get()
648+
testCrudRequestCheck(t, req, resp,
649+
err, 2)
650+
}
651+
652+
func TestCrudUpsertObjectSplice(t *testing.T) {
653+
test_helpers.SkipIfCrudSpliceBroken(t)
654+
655+
conn := connect(t)
656+
defer conn.Close()
657+
658+
req := crud.MakeUpsertObjectRequest(spaceName).
659+
Object(object).
660+
Operations([]crud.Operation{
661+
{
662+
Operator: crud.Splice,
663+
Field: "name",
664+
Pos: 1,
665+
Len: 1,
666+
Replace: "!!",
667+
},
668+
}).
669+
Opts(simpleOperationOpts)
670+
671+
testCrudRequestPrepareData(t, conn)
672+
resp, err := conn.Do(req).Get()
673+
testCrudRequestCheck(t, req, resp,
674+
err, 2)
675+
}
676+
545677
func TestUnflattenRows_IncorrectParams(t *testing.T) {
546678
invalidMetadata := []interface{}{
547679
map[interface{}]interface{}{

example_test.go

+11-16
Original file line numberDiff line numberDiff line change
@@ -439,34 +439,29 @@ func ExampleUpdateRequest() {
439439

440440
for i := 1111; i <= 1112; i++ {
441441
conn.Do(tarantool.NewReplaceRequest(spaceNo).
442-
Tuple([]interface{}{uint(i), "hello", "world"}),
442+
Tuple([]interface{}{uint(i), "text", 1, 1, 1, 1, 1}),
443443
).Get()
444444
}
445445

446446
req := tarantool.NewUpdateRequest(617).
447447
Key(tarantool.IntKey{1111}).
448-
Operations(tarantool.NewOperations().Assign(1, "bye"))
448+
Operations(tarantool.NewOperations().
449+
Add(2, 1).
450+
Subtract(3, 1).
451+
BitwiseAnd(4, 1).
452+
BitwiseOr(5, 1).
453+
BitwiseXor(6, 1).
454+
Splice(1, 1, 2, "!!").
455+
Insert(7, "new").
456+
Assign(7, "updated"))
449457
resp, err := conn.Do(req).Get()
450458
if err != nil {
451459
fmt.Printf("error in do update request is %v", err)
452460
return
453461
}
454462
fmt.Printf("response is %#v\n", resp.Data)
455-
456-
req = tarantool.NewUpdateRequest("test").
457-
Index("primary").
458-
Key(tarantool.IntKey{1111}).
459-
Operations(tarantool.NewOperations().Assign(1, "hello"))
460-
fut := conn.Do(req)
461-
resp, err = fut.Get()
462-
if err != nil {
463-
fmt.Printf("error in do async update request is %v", err)
464-
return
465-
}
466-
fmt.Printf("response is %#v\n", resp.Data)
467463
// Output:
468-
// response is []interface {}{[]interface {}{0x457, "bye", "world"}}
469-
// response is []interface {}{[]interface {}{0x457, "hello", "world"}}
464+
// response is []interface {}{[]interface {}{0x457, "t!!t", 2, 0, 1, 1, 0, "updated"}}
470465
}
471466

472467
func ExampleUpdateRequest_spaceAndIndexNames() {

request_test.go

+11-11
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,16 @@ func assertBodyEqual(t testing.TB, reference []byte, req Request) {
120120

121121
func getTestOps() ([]Op, *Operations) {
122122
ops := []Op{
123-
{"+", 1, 2},
124-
{"-", 3, 4},
125-
{"&", 5, 6},
126-
{"|", 7, 8},
127-
{"^", 9, 1},
128-
{"^", 9, 1}, // The duplication is for test purposes.
129-
{":", 2, 3},
130-
{"!", 4, 5},
131-
{"#", 6, 7},
132-
{"=", 8, 9},
123+
{Op: "+", Field: 1, Arg: 2},
124+
{Op: "-", Field: 3, Arg: 4},
125+
{Op: "&", Field: 5, Arg: 6},
126+
{Op: "|", Field: 7, Arg: 8},
127+
{Op: "^", Field: 9, Arg: 1},
128+
{Op: "^", Field: 9, Arg: 1}, // The duplication is for test purposes.
129+
{Op: ":", Field: 2, Pos: 3, Len: 1, Replace: "!!"},
130+
{Op: "!", Field: 4, Arg: 5},
131+
{Op: "#", Field: 6, Arg: 7},
132+
{Op: "=", Field: 8, Arg: 9},
133133
}
134134
operations := NewOperations().
135135
Add(1, 2).
@@ -138,7 +138,7 @@ func getTestOps() ([]Op, *Operations) {
138138
BitwiseOr(7, 8).
139139
BitwiseXor(9, 1).
140140
BitwiseXor(9, 1). // The duplication is for test purposes.
141-
Splice(2, 3).
141+
Splice(2, 3, 1, "!!").
142142
Insert(4, 5).
143143
Delete(6, 7).
144144
Assign(8, 9)

0 commit comments

Comments
 (0)