From 906765f7b0a10db9c85cca8258e1012aa3d3c05d Mon Sep 17 00:00:00 2001 From: Oleg Jukovec Date: Mon, 29 Jan 2024 09:24:41 +0300 Subject: [PATCH] crud: allow interface{} as values for *ManyRequest It was a mistake to use `[]interface{}` or `[]msgpack.CustomEncoder` as types for an array of tuples or an array of objects. Users were unable to use slices of custom types as incoming values. The patch now allows the use of `interface{}` as incoming values. It makes it easier to use the API, but users need to be more careful. Therefore, we have also added examples. Closes #365 --- CHANGELOG.md | 2 ++ README.md | 4 +++ crud/example_test.go | 83 ++++++++++++++++++++++++++++++++++++++++++++ crud/insert_many.go | 12 +++---- crud/object.go | 15 +++++--- crud/replace_many.go | 12 +++---- crud/tuple.go | 10 +++++- 7 files changed, 121 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c9def351..c1b1fc170 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -131,6 +131,8 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. - Tests with crud 1.4.0 (#336) - Tests with case sensitive SQL (#341) - Splice update operation accepts 3 arguments instead of 5 (#348) +- Unable to use a slice of custom types as a slice of tuples or objects for + `crud.*ManyRequest/crud.*ObjectManyRequest` (#365) ## [1.12.0] - 2023-06-07 diff --git a/README.md b/README.md index f37b3be64..e9989a88a 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,10 @@ The subpackage has been deleted. You could use `pool` instead. * `crud` operations `Timeout` option has `crud.OptFloat64` type instead of `crud.OptUint`. +* A slice of a custom type could be used as tuples for `ReplaceManyRequest` and + `InsertManyRequest`, `ReplaceObjectManyRequest`. +* A slice of a custom type could be used as objects for `ReplaceObjectManyRequest` + and `InsertObjectManyRequest`. #### test_helpers package diff --git a/crud/example_test.go b/crud/example_test.go index c043bd5f0..7f9e34e0c 100644 --- a/crud/example_test.go +++ b/crud/example_test.go @@ -85,6 +85,89 @@ func ExampleResult_rowsCustomType() { // [{{} 2010 45 bla}] } +// ExampleTuples_customType demonstrates how to use a slice of objects of a +// custom type as Tuples to make a ReplaceManyRequest. +func ExampleTuples_customType() { + conn := exampleConnect() + + // The type will be encoded/decoded as an array. + type Tuple struct { + _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused + Id uint64 + BucketId *uint64 + Name string + } + req := crud.MakeReplaceManyRequest(exampleSpace).Tuples([]Tuple{ + Tuple{ + Id: 2010, + BucketId: nil, + Name: "bla", + }, + }) + + ret := crud.MakeResult(reflect.TypeOf(Tuple{})) + if err := conn.Do(req).GetTyped(&ret); err != nil { + fmt.Printf("Failed to execute request: %s", err) + return + } + + fmt.Println(ret.Metadata) + rows := ret.Rows.([]Tuple) + if len(rows) == 1 { + fmt.Println(rows[0].Id) + fmt.Println(*rows[0].BucketId) + fmt.Println(rows[0].Name) + } else { + fmt.Printf("Unexpected result tuples count: %d", len(rows)) + } + // Output: + // [{id unsigned false} {bucket_id unsigned true} {name string false}] + // 2010 + // 45 + // bla +} + +// ExampleObjects_customType demonstrates how to use a slice of objects of +// a custom type as Objects to make a ReplaceObjectManyRequest. +func ExampleObjects_customType() { + conn := exampleConnect() + + // The type will be encoded/decoded as a map. + type Tuple struct { + Id uint64 `msgpack:"id,omitempty"` + BucketId *uint64 `msgpack:"bucket_id,omitempty"` + Name string `msgpack:"name,omitempty"` + } + req := crud.MakeReplaceObjectManyRequest(exampleSpace).Objects([]Tuple{ + Tuple{ + Id: 2010, + BucketId: nil, + Name: "bla", + }, + }) + + ret := crud.MakeResult(reflect.TypeOf(Tuple{})) + if err := conn.Do(req).GetTyped(&ret); err != nil { + fmt.Printf("Failed to execute request: %s", err) + return + } + + fmt.Println(ret.Metadata) + rows := ret.Rows.([]Tuple) + if len(rows) == 1 { + fmt.Println(rows[0].Id) + fmt.Println(*rows[0].BucketId) + fmt.Println(rows[0].Name) + } else { + fmt.Printf("Unexpected result tuples count: %d", len(rows)) + } + // Output: + // [{id unsigned false} {bucket_id unsigned true} {name string false}] + // 2010 + // 45 + // bla +} + // ExampleResult_operationData demonstrates how to obtain information // about erroneous objects from crud.Error using `OperationData` field. func ExampleResult_operationData() { diff --git a/crud/insert_many.go b/crud/insert_many.go index 602e210d5..866c1ceb5 100644 --- a/crud/insert_many.go +++ b/crud/insert_many.go @@ -15,14 +15,14 @@ type InsertManyOpts = OperationManyOpts // `crud.insert_many` for execution by a Connection. type InsertManyRequest struct { spaceRequest - tuples []Tuple + tuples Tuples opts InsertManyOpts } type insertManyArgs struct { _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused Space string - Tuples []Tuple + Tuples Tuples Opts InsertManyOpts } @@ -37,7 +37,7 @@ func MakeInsertManyRequest(space string) InsertManyRequest { // Tuples sets the tuples for the InsertManyRequest request. // Note: default value is nil. -func (req InsertManyRequest) Tuples(tuples []Tuple) InsertManyRequest { +func (req InsertManyRequest) Tuples(tuples Tuples) InsertManyRequest { req.tuples = tuples return req } @@ -73,14 +73,14 @@ type InsertObjectManyOpts = OperationObjectManyOpts // `crud.insert_object_many` for execution by a Connection. type InsertObjectManyRequest struct { spaceRequest - objects []Object + objects Objects opts InsertObjectManyOpts } type insertObjectManyArgs struct { _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused Space string - Objects []Object + Objects Objects Opts InsertObjectManyOpts } @@ -95,7 +95,7 @@ func MakeInsertObjectManyRequest(space string) InsertObjectManyRequest { // Objects sets the objects for the InsertObjectManyRequest request. // Note: default value is nil. -func (req InsertObjectManyRequest) Objects(objects []Object) InsertObjectManyRequest { +func (req InsertObjectManyRequest) Objects(objects Objects) InsertObjectManyRequest { req.objects = objects return req } diff --git a/crud/object.go b/crud/object.go index 3f266a7ee..8803d1268 100644 --- a/crud/object.go +++ b/crud/object.go @@ -4,10 +4,17 @@ import ( "github.com/vmihailenco/msgpack/v5" ) -// Object is an interface to describe object for CRUD methods. -type Object interface { - EncodeMsgpack(enc *msgpack.Encoder) -} +// Object is an interface to describe object for CRUD methods. It can be any +// type that msgpack can encode as a map. +type Object = interface{} + +// Objects is a type to describe an array of object for CRUD methods. It can be +// any type that msgpack can encode, but encoded data must be an array of +// objects. +// +// See the reason why not just []Object: +// https://github.com/tarantool/go-tarantool/issues/365 +type Objects = interface{} // MapObject is a type to describe object as a map. type MapObject map[string]interface{} diff --git a/crud/replace_many.go b/crud/replace_many.go index 77c947718..5a5143ef8 100644 --- a/crud/replace_many.go +++ b/crud/replace_many.go @@ -15,14 +15,14 @@ type ReplaceManyOpts = OperationManyOpts // `crud.replace_many` for execution by a Connection. type ReplaceManyRequest struct { spaceRequest - tuples []Tuple + tuples Tuples opts ReplaceManyOpts } type replaceManyArgs struct { _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused Space string - Tuples []Tuple + Tuples Tuples Opts ReplaceManyOpts } @@ -37,7 +37,7 @@ func MakeReplaceManyRequest(space string) ReplaceManyRequest { // Tuples sets the tuples for the ReplaceManyRequest request. // Note: default value is nil. -func (req ReplaceManyRequest) Tuples(tuples []Tuple) ReplaceManyRequest { +func (req ReplaceManyRequest) Tuples(tuples Tuples) ReplaceManyRequest { req.tuples = tuples return req } @@ -73,14 +73,14 @@ type ReplaceObjectManyOpts = OperationObjectManyOpts // `crud.replace_object_many` for execution by a Connection. type ReplaceObjectManyRequest struct { spaceRequest - objects []Object + objects Objects opts ReplaceObjectManyOpts } type replaceObjectManyArgs struct { _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused Space string - Objects []Object + Objects Objects Opts ReplaceObjectManyOpts } @@ -95,7 +95,7 @@ func MakeReplaceObjectManyRequest(space string) ReplaceObjectManyRequest { // Objects sets the tuple for the ReplaceObjectManyRequest request. // Note: default value is nil. -func (req ReplaceObjectManyRequest) Objects(objects []Object) ReplaceObjectManyRequest { +func (req ReplaceObjectManyRequest) Objects(objects Objects) ReplaceObjectManyRequest { req.objects = objects return req } diff --git a/crud/tuple.go b/crud/tuple.go index 61291cbb0..1f6850521 100644 --- a/crud/tuple.go +++ b/crud/tuple.go @@ -1,5 +1,13 @@ package crud // Tuple is a type to describe tuple for CRUD methods. It can be any type that -// msgpask can encode. +// msgpask can encode as an array. type Tuple = interface{} + +// Tuples is a type to describe an array of tuples for CRUD methods. It can be +// any type that msgpack can encode, but encoded data must be an array of +// tuples. +// +// See the reason why not just []Tuple: +// https://github.com/tarantool/go-tarantool/issues/365 +type Tuples = interface{}