diff --git a/.github/workflows/reusable_testing.yml b/.github/workflows/reusable_testing.yml
index 94c671543..beb4c84c8 100644
--- a/.github/workflows/reusable_testing.yml
+++ b/.github/workflows/reusable_testing.yml
@@ -36,7 +36,7 @@ jobs:
- name: Setup golang for connector and tests
uses: actions/setup-go@v5
with:
- go-version: '1.20'
+ go-version: '1.24'
- name: Setup tt
run: |
diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml
index 2fce2f5ec..12e41dae6 100644
--- a/.github/workflows/testing.yml
+++ b/.github/workflows/testing.yml
@@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- golang: ['1.20', 'stable']
+ golang: ['1.24', 'stable']
tarantool: ['1.10']
coveralls: [false]
fuzzing: [false]
@@ -36,19 +36,19 @@ jobs:
strategy:
fail-fast: false
matrix:
- golang: ['1.20', 'stable']
+ golang: ['1.24', 'stable']
tarantool: ['2.11', '3.4', 'master']
coveralls: [false]
fuzzing: [false]
include:
- - golang: '1.20'
+ - golang: '1.24'
tarantool: 'master'
coveralls: true
fuzzing: false
- - golang: '1.20'
+ - golang: '1.24'
tarantool: 'master'
- fuzzing: true
coveralls: false
+ fuzzing: true
uses: ./.github/workflows/reusable-run.yml
with:
os: ubuntu-24.04
@@ -72,7 +72,7 @@ jobs:
fail-fast: false
matrix:
golang:
- - '1.20'
+ - '1.24'
- 'stable'
runs-on:
- macos-14
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 737db9029..ab3c73982 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,8 +10,12 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
### Added
+* New types for MessagePack extensions compatible with go-option (#459).
+
### Changed
+* Required Go version is `1.24` now (#456).
+
### Fixed
## [v2.4.1] - 2025-10-16
diff --git a/MIGRATION.md b/MIGRATION.md
index fdd4893dc..54f4e3034 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -1,8 +1,18 @@
# Migration guide
+## Migration from v2.x.x to v3.x.x
+
+* [Major changes](#major-changes-v3)
+
+TODO
+
+### Major changes
+
+* Required Go version is `1.24` now.
+
## Migration from v1.x.x to v2.x.x
-* [Major changes](#major-changes)
+* [Major changes](#major-changes-v2)
* [Main package](#main-package)
* [Go version](#go-version)
* [msgpack/v5](#msgpackv5)
@@ -25,7 +35,7 @@
* [crud package](#crud-package)
* [test_helpers package](#test_helpers-package)
-### Major changes
+### Major changes
* The `go_tarantool_call_17` build tag is no longer needed, since by default
the `CallRequest` is `Call17Request`.
@@ -50,9 +60,9 @@ import (
"fmt"
"github.com/tarantool/go-tarantool"
- _ "github.com/tarantool/go-tarantool/v2/datetime"
- _ "github.com/tarantool/go-tarantool/v2/decimal"
- _ "github.com/tarantool/go-tarantool/v2/uuid"
+ _ "github.com/tarantool/go-tarantool/v3/datetime"
+ _ "github.com/tarantool/go-tarantool/v3/decimal"
+ _ "github.com/tarantool/go-tarantool/v3/uuid"
)
func main() {
@@ -82,10 +92,10 @@ import (
"fmt"
"time"
- "github.com/tarantool/go-tarantool/v2"
- _ "github.com/tarantool/go-tarantool/v2/datetime"
- _ "github.com/tarantool/go-tarantool/v2/decimal"
- _ "github.com/tarantool/go-tarantool/v2/uuid"
+ "github.com/tarantool/go-tarantool/v3"
+ _ "github.com/tarantool/go-tarantool/v3/datetime"
+ _ "github.com/tarantool/go-tarantool/v3/decimal"
+ _ "github.com/tarantool/go-tarantool/v3/uuid"
)
func main() {
diff --git a/README.md b/README.md
index f5c533390..6950724ca 100644
--- a/README.md
+++ b/README.md
@@ -36,10 +36,10 @@ faster than other packages according to public benchmarks.
We assume that you have Tarantool version 1.10+ and a modern Linux or BSD
operating system.
-You need a current version of `go`, version 1.20 or later (use `go version` to
+You need a current version of `go`, version 1.24 or later (use `go version` to
check the version number). Do not use `gccgo-go`.
-**Note:** If your `go` version is older than 1.20 or if `go` is not installed,
+**Note:** If your `go` version is older than 1.24 or if `go` is not installed,
download and run the latest tarball from [golang.org][golang-dl].
The package `go-tarantool` is located in [tarantool/go-tarantool][go-tarantool]
@@ -98,10 +98,10 @@ import (
"fmt"
"time"
- "github.com/tarantool/go-tarantool/v2"
- _ "github.com/tarantool/go-tarantool/v2/datetime"
- _ "github.com/tarantool/go-tarantool/v2/decimal"
- _ "github.com/tarantool/go-tarantool/v2/uuid"
+ "github.com/tarantool/go-tarantool/v3"
+ _ "github.com/tarantool/go-tarantool/v3/datetime"
+ _ "github.com/tarantool/go-tarantool/v3/decimal"
+ _ "github.com/tarantool/go-tarantool/v3/uuid"
)
func main() {
@@ -184,10 +184,10 @@ import (
"fmt"
"time"
- "github.com/tarantool/go-tarantool/v2"
- _ "github.com/tarantool/go-tarantool/v2/datetime"
- _ "github.com/tarantool/go-tarantool/v2/decimal"
- _ "github.com/tarantool/go-tarantool/v2/uuid"
+ "github.com/tarantool/go-tarantool/v3"
+ _ "github.com/tarantool/go-tarantool/v3/datetime"
+ _ "github.com/tarantool/go-tarantool/v3/decimal"
+ _ "github.com/tarantool/go-tarantool/v3/uuid"
"github.com/tarantool/go-tlsdialer"
)
diff --git a/arrow/arrow.go b/arrow/arrow.go
index aaeaccca9..a7767c459 100644
--- a/arrow/arrow.go
+++ b/arrow/arrow.go
@@ -7,6 +7,8 @@ import (
"github.com/vmihailenco/msgpack/v5"
)
+//go:generate go tool gentypes -ext-code 8 Arrow
+
// Arrow MessagePack extension type.
const arrowExtId = 8
@@ -26,6 +28,17 @@ func (a Arrow) Raw() []byte {
return a.data
}
+// MarshalMsgpack implements a custom msgpack marshaler for extension type.
+func (a Arrow) MarshalMsgpack() ([]byte, error) {
+ return a.data, nil
+}
+
+// UnmarshalMsgpack implements a custom msgpack unmarshaler for extension type.
+func (a *Arrow) UnmarshalMsgpack(data []byte) error {
+ a.data = data
+ return nil
+}
+
func arrowDecoder(d *msgpack.Decoder, v reflect.Value, extLen int) error {
arrow := Arrow{
data: make([]byte, extLen),
diff --git a/arrow/arrow_gen.go b/arrow/arrow_gen.go
new file mode 100644
index 000000000..c86c72778
--- /dev/null
+++ b/arrow/arrow_gen.go
@@ -0,0 +1,241 @@
+// Code generated by github.com/tarantool/go-option; DO NOT EDIT.
+
+package arrow
+
+import (
+ "fmt"
+
+ "github.com/vmihailenco/msgpack/v5"
+ "github.com/vmihailenco/msgpack/v5/msgpcode"
+
+ "github.com/tarantool/go-option"
+)
+
+// OptionalArrow represents an optional value of type Arrow.
+// It can either hold a valid Arrow (IsSome == true) or be empty (IsZero == true).
+type OptionalArrow struct {
+ value Arrow
+ exists bool
+}
+
+// SomeOptionalArrow creates an optional OptionalArrow with the given Arrow value.
+// The returned OptionalArrow will have IsSome() == true and IsZero() == false.
+func SomeOptionalArrow(value Arrow) OptionalArrow {
+ return OptionalArrow{
+ value: value,
+ exists: true,
+ }
+}
+
+// NoneOptionalArrow creates an empty optional OptionalArrow value.
+// The returned OptionalArrow will have IsSome() == false and IsZero() == true.
+//
+// Example:
+//
+// o := NoneOptionalArrow()
+// if o.IsZero() {
+// fmt.Println("value is absent")
+// }
+func NoneOptionalArrow() OptionalArrow {
+ return OptionalArrow{}
+}
+
+func (o OptionalArrow) newEncodeError(err error) error {
+ if err == nil {
+ return nil
+ }
+ return &option.EncodeError{
+ Type: "OptionalArrow",
+ Parent: err,
+ }
+}
+
+func (o OptionalArrow) newDecodeError(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ return &option.DecodeError{
+ Type: "OptionalArrow",
+ Parent: err,
+ }
+}
+
+// IsSome returns true if the OptionalArrow contains a value.
+// This indicates the value is explicitly set (not None).
+func (o OptionalArrow) IsSome() bool {
+ return o.exists
+}
+
+// IsZero returns true if the OptionalArrow does not contain a value.
+// Equivalent to !IsSome(). Useful for consistency with types where
+// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished.
+func (o OptionalArrow) IsZero() bool {
+ return !o.exists
+}
+
+// IsNil is an alias for IsZero.
+//
+// This method is provided for compatibility with the msgpack Encoder interface.
+func (o OptionalArrow) IsNil() bool {
+ return o.IsZero()
+}
+
+// Get returns the stored value and a boolean flag indicating its presence.
+// If the value is present, returns (value, true).
+// If the value is absent, returns (zero value of Arrow, false).
+//
+// Recommended usage:
+//
+// if value, ok := o.Get(); ok {
+// // use value
+// }
+func (o OptionalArrow) Get() (Arrow, bool) {
+ return o.value, o.exists
+}
+
+// MustGet returns the stored value if it is present.
+// Panics if the value is absent (i.e., IsZero() == true).
+//
+// Use with caution — only when you are certain the value exists.
+//
+// Panics with: "optional value is not set" if no value is set.
+func (o OptionalArrow) MustGet() Arrow {
+ if !o.exists {
+ panic("optional value is not set")
+ }
+
+ return o.value
+}
+
+// Unwrap returns the stored value regardless of presence.
+// If no value is set, returns the zero value for Arrow.
+//
+// Warning: Does not check presence. Use IsSome() before calling if you need
+// to distinguish between absent value and explicit zero value.
+func (o OptionalArrow) Unwrap() Arrow {
+ return o.value
+}
+
+// UnwrapOr returns the stored value if present.
+// Otherwise, returns the provided default value.
+//
+// Example:
+//
+// o := NoneOptionalArrow()
+// v := o.UnwrapOr(someDefaultOptionalArrow)
+func (o OptionalArrow) UnwrapOr(defaultValue Arrow) Arrow {
+ if o.exists {
+ return o.value
+ }
+
+ return defaultValue
+}
+
+// UnwrapOrElse returns the stored value if present.
+// Otherwise, calls the provided function and returns its result.
+// Useful when the default value requires computation or side effects.
+//
+// Example:
+//
+// o := NoneOptionalArrow()
+// v := o.UnwrapOrElse(func() Arrow { return computeDefault() })
+func (o OptionalArrow) UnwrapOrElse(defaultValue func() Arrow) Arrow {
+ if o.exists {
+ return o.value
+ }
+
+ return defaultValue()
+}
+
+func (o OptionalArrow) encodeValue(encoder *msgpack.Encoder) error {
+ value, err := o.value.MarshalMsgpack()
+ if err != nil {
+ return err
+ }
+
+ err = encoder.EncodeExtHeader(8, len(value))
+ if err != nil {
+ return err
+ }
+
+ _, err = encoder.Writer().Write(value)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// EncodeMsgpack encodes the OptionalArrow value using MessagePack format.
+// - If the value is present, it is encoded as Arrow.
+// - If the value is absent (None), it is encoded as nil.
+//
+// Returns an error if encoding fails.
+func (o OptionalArrow) EncodeMsgpack(encoder *msgpack.Encoder) error {
+ if o.exists {
+ return o.newEncodeError(o.encodeValue(encoder))
+ }
+
+ return o.newEncodeError(encoder.EncodeNil())
+}
+
+func (o *OptionalArrow) decodeValue(decoder *msgpack.Decoder) error {
+ tp, length, err := decoder.DecodeExtHeader()
+ switch {
+ case err != nil:
+ return o.newDecodeError(err)
+ case tp != 8:
+ return o.newDecodeError(fmt.Errorf("invalid extension code: %d", tp))
+ }
+
+ a := make([]byte, length)
+ if err := decoder.ReadFull(a); err != nil {
+ return o.newDecodeError(err)
+ }
+
+ if err := o.value.UnmarshalMsgpack(a); err != nil {
+ return o.newDecodeError(err)
+ }
+
+ o.exists = true
+ return nil
+}
+
+func (o *OptionalArrow) checkCode(code byte) bool {
+ return msgpcode.IsExt(code)
+}
+
+// DecodeMsgpack decodes a OptionalArrow value from MessagePack format.
+// Supports two input types:
+// - nil: interpreted as no value (NoneOptionalArrow)
+// - Arrow: interpreted as a present value (SomeOptionalArrow)
+//
+// Returns an error if the input type is unsupported or decoding fails.
+//
+// After successful decoding:
+// - on nil: exists = false, value = default zero value
+// - on Arrow: exists = true, value = decoded value
+func (o *OptionalArrow) DecodeMsgpack(decoder *msgpack.Decoder) error {
+ code, err := decoder.PeekCode()
+ if err != nil {
+ return o.newDecodeError(err)
+ }
+
+ switch {
+ case code == msgpcode.Nil:
+ o.exists = false
+
+ return o.newDecodeError(decoder.Skip())
+ case o.checkCode(code):
+ err := o.decodeValue(decoder)
+ if err != nil {
+ return o.newDecodeError(err)
+ }
+ o.exists = true
+
+ return err
+ default:
+ return o.newDecodeError(fmt.Errorf("unexpected code: %d", code))
+ }
+}
diff --git a/arrow/arrow_gen_test.go b/arrow/arrow_gen_test.go
new file mode 100644
index 000000000..d990499f4
--- /dev/null
+++ b/arrow/arrow_gen_test.go
@@ -0,0 +1,124 @@
+package arrow
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/vmihailenco/msgpack/v5"
+)
+
+func TestSomeOptionalArrow(t *testing.T) {
+ val, err := MakeArrow([]byte{1, 2, 3})
+ assert.NoError(t, err)
+ opt := SomeOptionalArrow(val)
+
+ assert.True(t, opt.IsSome())
+ assert.False(t, opt.IsZero())
+
+ v, ok := opt.Get()
+ assert.True(t, ok)
+ assert.Equal(t, val, v)
+}
+
+func TestNoneOptionalArrow(t *testing.T) {
+ opt := NoneOptionalArrow()
+
+ assert.False(t, opt.IsSome())
+ assert.True(t, opt.IsZero())
+
+ _, ok := opt.Get()
+ assert.False(t, ok)
+}
+
+func TestOptionalArrow_MustGet(t *testing.T) {
+ val, err := MakeArrow([]byte{1, 2, 3})
+ assert.NoError(t, err)
+ optSome := SomeOptionalArrow(val)
+ optNone := NoneOptionalArrow()
+
+ assert.Equal(t, val, optSome.MustGet())
+ assert.Panics(t, func() { optNone.MustGet() })
+}
+
+func TestOptionalArrow_Unwrap(t *testing.T) {
+ val, err := MakeArrow([]byte{1, 2, 3})
+ assert.NoError(t, err)
+ optSome := SomeOptionalArrow(val)
+ optNone := NoneOptionalArrow()
+
+ assert.Equal(t, val, optSome.Unwrap())
+ assert.Equal(t, Arrow{}, optNone.Unwrap())
+}
+
+func TestOptionalArrow_UnwrapOr(t *testing.T) {
+ val, err := MakeArrow([]byte{1, 2, 3})
+ assert.NoError(t, err)
+ def, err := MakeArrow([]byte{4, 5, 6})
+ assert.NoError(t, err)
+ optSome := SomeOptionalArrow(val)
+ optNone := NoneOptionalArrow()
+
+ assert.Equal(t, val, optSome.UnwrapOr(def))
+ assert.Equal(t, def, optNone.UnwrapOr(def))
+}
+
+func TestOptionalArrow_UnwrapOrElse(t *testing.T) {
+ val, err := MakeArrow([]byte{1, 2, 3})
+ assert.NoError(t, err)
+ def, err := MakeArrow([]byte{4, 5, 6})
+ assert.NoError(t, err)
+ optSome := SomeOptionalArrow(val)
+ optNone := NoneOptionalArrow()
+
+ assert.Equal(t, val, optSome.UnwrapOrElse(func() Arrow { return def }))
+ assert.Equal(t, def, optNone.UnwrapOrElse(func() Arrow { return def }))
+}
+
+func TestOptionalArrow_EncodeDecodeMsgpack_Some(t *testing.T) {
+ val, err := MakeArrow([]byte{1, 2, 3})
+ assert.NoError(t, err)
+ some := SomeOptionalArrow(val)
+
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err = enc.Encode(some)
+ assert.NoError(t, err)
+
+ var decodedSome OptionalArrow
+ err = dec.Decode(&decodedSome)
+ assert.NoError(t, err)
+ assert.True(t, decodedSome.IsSome())
+ assert.Equal(t, val, decodedSome.Unwrap())
+}
+
+func TestOptionalArrow_EncodeDecodeMsgpack_None(t *testing.T) {
+ none := NoneOptionalArrow()
+
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(none)
+ assert.NoError(t, err)
+
+ var decodedNone OptionalArrow
+ err = dec.Decode(&decodedNone)
+ assert.NoError(t, err)
+ assert.True(t, decodedNone.IsZero())
+}
+
+func TestOptionalArrow_EncodeDecodeMsgpack_InvalidType(t *testing.T) {
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(123)
+ assert.NoError(t, err)
+
+ var decodedInvalid OptionalArrow
+ err = dec.Decode(&decodedInvalid)
+ assert.Error(t, err)
+}
diff --git a/arrow/arrow_test.go b/arrow/arrow_test.go
index 5e8b440c4..c3f40090c 100644
--- a/arrow/arrow_test.go
+++ b/arrow/arrow_test.go
@@ -6,8 +6,9 @@ import (
"testing"
"github.com/stretchr/testify/require"
- "github.com/tarantool/go-tarantool/v2/arrow"
"github.com/vmihailenco/msgpack/v5"
+
+ "github.com/tarantool/go-tarantool/v3/arrow"
)
var longArrow, _ = hex.DecodeString("ffffffff70000000040000009effffff0400010004000000" +
diff --git a/arrow/example_test.go b/arrow/example_test.go
index e85d195cd..3510a777d 100644
--- a/arrow/example_test.go
+++ b/arrow/example_test.go
@@ -15,8 +15,8 @@ import (
"log"
"time"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/arrow"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/arrow"
)
var arrowBinData, _ = hex.DecodeString("ffffffff70000000040000009effffff0400010004000000" +
diff --git a/arrow/request.go b/arrow/request.go
index 332720d3e..82b55f399 100644
--- a/arrow/request.go
+++ b/arrow/request.go
@@ -5,8 +5,9 @@ import (
"io"
"github.com/tarantool/go-iproto"
- "github.com/tarantool/go-tarantool/v2"
"github.com/vmihailenco/msgpack/v5"
+
+ "github.com/tarantool/go-tarantool/v3"
)
// InsertRequest helps you to create an insert request object for execution
diff --git a/arrow/request_test.go b/arrow/request_test.go
index fba6b5563..251b2b31d 100644
--- a/arrow/request_test.go
+++ b/arrow/request_test.go
@@ -8,9 +8,10 @@ import (
"github.com/stretchr/testify/require"
"github.com/tarantool/go-iproto"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/arrow"
"github.com/vmihailenco/msgpack/v5"
+
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/arrow"
)
const validSpace uint32 = 1 // Any valid value != default.
diff --git a/arrow/tarantool_test.go b/arrow/tarantool_test.go
index 728b38f8b..cc2ad552e 100644
--- a/arrow/tarantool_test.go
+++ b/arrow/tarantool_test.go
@@ -11,9 +11,9 @@ import (
"github.com/stretchr/testify/require"
"github.com/tarantool/go-iproto"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/arrow"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/arrow"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
var isArrowSupported = false
diff --git a/auth_test.go b/auth_test.go
index 712226d63..a9a0b34e9 100644
--- a/auth_test.go
+++ b/auth_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
- . "github.com/tarantool/go-tarantool/v2"
+ . "github.com/tarantool/go-tarantool/v3"
)
func TestAuth_String(t *testing.T) {
diff --git a/box/box.go b/box/box.go
index c9b0e71dd..a4cc1780e 100644
--- a/box/box.go
+++ b/box/box.go
@@ -1,7 +1,7 @@
package box
import (
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// Box is a helper that wraps box.* requests.
diff --git a/box/box_test.go b/box/box_test.go
index 0180473ed..59ceaf6e3 100644
--- a/box/box_test.go
+++ b/box/box_test.go
@@ -7,8 +7,9 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "github.com/tarantool/go-tarantool/v2/box"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+
+ "github.com/tarantool/go-tarantool/v3/box"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
func TestNew(t *testing.T) {
diff --git a/box/example_test.go b/box/example_test.go
index eac3f5e1a..fa65189a8 100644
--- a/box/example_test.go
+++ b/box/example_test.go
@@ -15,8 +15,8 @@ import (
"log"
"time"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/box"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/box"
)
func ExampleBox_Info() {
diff --git a/box/info.go b/box/info.go
index aa682822c..edd4894cd 100644
--- a/box/info.go
+++ b/box/info.go
@@ -3,8 +3,9 @@ package box
import (
"fmt"
- "github.com/tarantool/go-tarantool/v2"
"github.com/vmihailenco/msgpack/v5"
+
+ "github.com/tarantool/go-tarantool/v3"
)
var _ tarantool.Request = (*InfoRequest)(nil)
diff --git a/box/info_test.go b/box/info_test.go
index 2cf00f69b..d4e8e9539 100644
--- a/box/info_test.go
+++ b/box/info_test.go
@@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2/box"
+ "github.com/tarantool/go-tarantool/v3/box"
)
func TestInfo(t *testing.T) {
diff --git a/box/schema.go b/box/schema.go
index 25017e5a4..036c76c6d 100644
--- a/box/schema.go
+++ b/box/schema.go
@@ -1,6 +1,8 @@
package box
-import "github.com/tarantool/go-tarantool/v2"
+import (
+ "github.com/tarantool/go-tarantool/v3"
+)
// Schema represents the schema-related operations in Tarantool.
// It holds a connection to interact with the Tarantool instance.
diff --git a/box/schema_user.go b/box/schema_user.go
index 80874da92..6e9f558a5 100644
--- a/box/schema_user.go
+++ b/box/schema_user.go
@@ -5,8 +5,9 @@ import (
"fmt"
"strings"
- "github.com/tarantool/go-tarantool/v2"
"github.com/vmihailenco/msgpack/v5"
+
+ "github.com/tarantool/go-tarantool/v3"
)
// SchemaUser provides methods to interact with schema-related user operations in Tarantool.
diff --git a/box/schema_user_test.go b/box/schema_user_test.go
index b9f3e18de..985e9fe86 100644
--- a/box/schema_user_test.go
+++ b/box/schema_user_test.go
@@ -7,11 +7,11 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "github.com/tarantool/go-tarantool/v2"
"github.com/vmihailenco/msgpack/v5"
"github.com/vmihailenco/msgpack/v5/msgpcode"
- "github.com/tarantool/go-tarantool/v2/box"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/box"
)
func TestUserExistsResponse_DecodeMsgpack(t *testing.T) {
diff --git a/box/session.go b/box/session.go
index b63113581..5a01b32a3 100644
--- a/box/session.go
+++ b/box/session.go
@@ -3,7 +3,7 @@ package box
import (
"context"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// Session struct represents a connection session to Tarantool.
diff --git a/box/session_test.go b/box/session_test.go
index 07ada0436..39b80d1d4 100644
--- a/box/session_test.go
+++ b/box/session_test.go
@@ -5,8 +5,8 @@ import (
"github.com/stretchr/testify/require"
- "github.com/tarantool/go-tarantool/v2/box"
- th "github.com/tarantool/go-tarantool/v2/test_helpers"
+ "github.com/tarantool/go-tarantool/v3/box"
+ th "github.com/tarantool/go-tarantool/v3/test_helpers"
)
func TestBox_Session(t *testing.T) {
diff --git a/box/tarantool_test.go b/box/tarantool_test.go
index 0eb9e94b9..4244a98e9 100644
--- a/box/tarantool_test.go
+++ b/box/tarantool_test.go
@@ -11,9 +11,10 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/tarantool/go-iproto"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/box"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/box"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
var server = "127.0.0.1:3013"
diff --git a/box_error.go b/boxerror.go
similarity index 99%
rename from box_error.go
rename to boxerror.go
index 59bfb4a05..2fb18268f 100644
--- a/box_error.go
+++ b/boxerror.go
@@ -9,6 +9,8 @@ import (
const errorExtID = 3
+//go:generate go tool gentypes -ext-code 3 BoxError
+
const (
keyErrorStack = 0x00
keyErrorType = 0x00
diff --git a/boxerror_gen.go b/boxerror_gen.go
new file mode 100644
index 000000000..07a1be695
--- /dev/null
+++ b/boxerror_gen.go
@@ -0,0 +1,241 @@
+// Code generated by github.com/tarantool/go-option; DO NOT EDIT.
+
+package tarantool
+
+import (
+ "fmt"
+
+ "github.com/vmihailenco/msgpack/v5"
+ "github.com/vmihailenco/msgpack/v5/msgpcode"
+
+ "github.com/tarantool/go-option"
+)
+
+// OptionalBoxError represents an optional value of type BoxError.
+// It can either hold a valid BoxError (IsSome == true) or be empty (IsZero == true).
+type OptionalBoxError struct {
+ value BoxError
+ exists bool
+}
+
+// SomeOptionalBoxError creates an optional OptionalBoxError with the given BoxError value.
+// The returned OptionalBoxError will have IsSome() == true and IsZero() == false.
+func SomeOptionalBoxError(value BoxError) OptionalBoxError {
+ return OptionalBoxError{
+ value: value,
+ exists: true,
+ }
+}
+
+// NoneOptionalBoxError creates an empty optional OptionalBoxError value.
+// The returned OptionalBoxError will have IsSome() == false and IsZero() == true.
+//
+// Example:
+//
+// o := NoneOptionalBoxError()
+// if o.IsZero() {
+// fmt.Println("value is absent")
+// }
+func NoneOptionalBoxError() OptionalBoxError {
+ return OptionalBoxError{}
+}
+
+func (o OptionalBoxError) newEncodeError(err error) error {
+ if err == nil {
+ return nil
+ }
+ return &option.EncodeError{
+ Type: "OptionalBoxError",
+ Parent: err,
+ }
+}
+
+func (o OptionalBoxError) newDecodeError(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ return &option.DecodeError{
+ Type: "OptionalBoxError",
+ Parent: err,
+ }
+}
+
+// IsSome returns true if the OptionalBoxError contains a value.
+// This indicates the value is explicitly set (not None).
+func (o OptionalBoxError) IsSome() bool {
+ return o.exists
+}
+
+// IsZero returns true if the OptionalBoxError does not contain a value.
+// Equivalent to !IsSome(). Useful for consistency with types where
+// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished.
+func (o OptionalBoxError) IsZero() bool {
+ return !o.exists
+}
+
+// IsNil is an alias for IsZero.
+//
+// This method is provided for compatibility with the msgpack Encoder interface.
+func (o OptionalBoxError) IsNil() bool {
+ return o.IsZero()
+}
+
+// Get returns the stored value and a boolean flag indicating its presence.
+// If the value is present, returns (value, true).
+// If the value is absent, returns (zero value of BoxError, false).
+//
+// Recommended usage:
+//
+// if value, ok := o.Get(); ok {
+// // use value
+// }
+func (o OptionalBoxError) Get() (BoxError, bool) {
+ return o.value, o.exists
+}
+
+// MustGet returns the stored value if it is present.
+// Panics if the value is absent (i.e., IsZero() == true).
+//
+// Use with caution — only when you are certain the value exists.
+//
+// Panics with: "optional value is not set" if no value is set.
+func (o OptionalBoxError) MustGet() BoxError {
+ if !o.exists {
+ panic("optional value is not set")
+ }
+
+ return o.value
+}
+
+// Unwrap returns the stored value regardless of presence.
+// If no value is set, returns the zero value for BoxError.
+//
+// Warning: Does not check presence. Use IsSome() before calling if you need
+// to distinguish between absent value and explicit zero value.
+func (o OptionalBoxError) Unwrap() BoxError {
+ return o.value
+}
+
+// UnwrapOr returns the stored value if present.
+// Otherwise, returns the provided default value.
+//
+// Example:
+//
+// o := NoneOptionalBoxError()
+// v := o.UnwrapOr(someDefaultOptionalBoxError)
+func (o OptionalBoxError) UnwrapOr(defaultValue BoxError) BoxError {
+ if o.exists {
+ return o.value
+ }
+
+ return defaultValue
+}
+
+// UnwrapOrElse returns the stored value if present.
+// Otherwise, calls the provided function and returns its result.
+// Useful when the default value requires computation or side effects.
+//
+// Example:
+//
+// o := NoneOptionalBoxError()
+// v := o.UnwrapOrElse(func() BoxError { return computeDefault() })
+func (o OptionalBoxError) UnwrapOrElse(defaultValue func() BoxError) BoxError {
+ if o.exists {
+ return o.value
+ }
+
+ return defaultValue()
+}
+
+func (o OptionalBoxError) encodeValue(encoder *msgpack.Encoder) error {
+ value, err := o.value.MarshalMsgpack()
+ if err != nil {
+ return err
+ }
+
+ err = encoder.EncodeExtHeader(3, len(value))
+ if err != nil {
+ return err
+ }
+
+ _, err = encoder.Writer().Write(value)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// EncodeMsgpack encodes the OptionalBoxError value using MessagePack format.
+// - If the value is present, it is encoded as BoxError.
+// - If the value is absent (None), it is encoded as nil.
+//
+// Returns an error if encoding fails.
+func (o OptionalBoxError) EncodeMsgpack(encoder *msgpack.Encoder) error {
+ if o.exists {
+ return o.newEncodeError(o.encodeValue(encoder))
+ }
+
+ return o.newEncodeError(encoder.EncodeNil())
+}
+
+func (o *OptionalBoxError) decodeValue(decoder *msgpack.Decoder) error {
+ tp, length, err := decoder.DecodeExtHeader()
+ switch {
+ case err != nil:
+ return o.newDecodeError(err)
+ case tp != 3:
+ return o.newDecodeError(fmt.Errorf("invalid extension code: %d", tp))
+ }
+
+ a := make([]byte, length)
+ if err := decoder.ReadFull(a); err != nil {
+ return o.newDecodeError(err)
+ }
+
+ if err := o.value.UnmarshalMsgpack(a); err != nil {
+ return o.newDecodeError(err)
+ }
+
+ o.exists = true
+ return nil
+}
+
+func (o *OptionalBoxError) checkCode(code byte) bool {
+ return msgpcode.IsExt(code)
+}
+
+// DecodeMsgpack decodes a OptionalBoxError value from MessagePack format.
+// Supports two input types:
+// - nil: interpreted as no value (NoneOptionalBoxError)
+// - BoxError: interpreted as a present value (SomeOptionalBoxError)
+//
+// Returns an error if the input type is unsupported or decoding fails.
+//
+// After successful decoding:
+// - on nil: exists = false, value = default zero value
+// - on BoxError: exists = true, value = decoded value
+func (o *OptionalBoxError) DecodeMsgpack(decoder *msgpack.Decoder) error {
+ code, err := decoder.PeekCode()
+ if err != nil {
+ return o.newDecodeError(err)
+ }
+
+ switch {
+ case code == msgpcode.Nil:
+ o.exists = false
+
+ return o.newDecodeError(decoder.Skip())
+ case o.checkCode(code):
+ err := o.decodeValue(decoder)
+ if err != nil {
+ return o.newDecodeError(err)
+ }
+ o.exists = true
+
+ return err
+ default:
+ return o.newDecodeError(fmt.Errorf("unexpected code: %d", code))
+ }
+}
diff --git a/boxerror_gen_test.go b/boxerror_gen_test.go
new file mode 100644
index 000000000..4d7fada3a
--- /dev/null
+++ b/boxerror_gen_test.go
@@ -0,0 +1,116 @@
+package tarantool
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/vmihailenco/msgpack/v5"
+)
+
+func TestSomeOptionalBoxError(t *testing.T) {
+ val := BoxError{Code: 1, Msg: "error"}
+ opt := SomeOptionalBoxError(val)
+
+ assert.True(t, opt.IsSome())
+ assert.False(t, opt.IsZero())
+
+ v, ok := opt.Get()
+ assert.True(t, ok)
+ assert.Equal(t, val, v)
+}
+
+func TestNoneOptionalBoxError(t *testing.T) {
+ opt := NoneOptionalBoxError()
+
+ assert.False(t, opt.IsSome())
+ assert.True(t, opt.IsZero())
+
+ _, ok := opt.Get()
+ assert.False(t, ok)
+}
+
+func TestOptionalBoxError_MustGet(t *testing.T) {
+ val := BoxError{Code: 1, Msg: "error"}
+ optSome := SomeOptionalBoxError(val)
+ optNone := NoneOptionalBoxError()
+
+ assert.Equal(t, val, optSome.MustGet())
+ assert.Panics(t, func() { optNone.MustGet() })
+}
+
+func TestOptionalBoxError_Unwrap(t *testing.T) {
+ val := BoxError{Code: 1, Msg: "error"}
+ optSome := SomeOptionalBoxError(val)
+ optNone := NoneOptionalBoxError()
+
+ assert.Equal(t, val, optSome.Unwrap())
+ assert.Equal(t, BoxError{}, optNone.Unwrap())
+}
+
+func TestOptionalBoxError_UnwrapOr(t *testing.T) {
+ val := BoxError{Code: 1, Msg: "error"}
+ def := BoxError{Code: 2, Msg: "default"}
+ optSome := SomeOptionalBoxError(val)
+ optNone := NoneOptionalBoxError()
+
+ assert.Equal(t, val, optSome.UnwrapOr(def))
+ assert.Equal(t, def, optNone.UnwrapOr(def))
+}
+
+func TestOptionalBoxError_UnwrapOrElse(t *testing.T) {
+ val := BoxError{Code: 1, Msg: "error"}
+ def := BoxError{Code: 2, Msg: "default"}
+ optSome := SomeOptionalBoxError(val)
+ optNone := NoneOptionalBoxError()
+
+ assert.Equal(t, val, optSome.UnwrapOrElse(func() BoxError { return def }))
+ assert.Equal(t, def, optNone.UnwrapOrElse(func() BoxError { return def }))
+}
+
+func TestOptionalBoxError_EncodeDecodeMsgpack_Some(t *testing.T) {
+ val := BoxError{Code: 1, Msg: "error"}
+ some := SomeOptionalBoxError(val)
+
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(some)
+ assert.NoError(t, err)
+
+ var decodedSome OptionalBoxError
+ err = dec.Decode(&decodedSome)
+ assert.NoError(t, err)
+ assert.True(t, decodedSome.IsSome())
+ assert.Equal(t, val, decodedSome.Unwrap())
+}
+
+func TestOptionalBoxError_EncodeDecodeMsgpack_None(t *testing.T) {
+ none := NoneOptionalBoxError()
+
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(none)
+ assert.NoError(t, err)
+
+ var decodedNone OptionalBoxError
+ err = dec.Decode(&decodedNone)
+ assert.NoError(t, err)
+ assert.True(t, decodedNone.IsZero())
+}
+
+func TestOptionalBoxError_EncodeDecodeMsgpack_InvalidType(t *testing.T) {
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(123)
+ assert.NoError(t, err)
+
+ var decodedInvalid OptionalBoxError
+ err = dec.Decode(&decodedInvalid)
+ assert.Error(t, err)
+}
diff --git a/box_error_test.go b/boxerror_test.go
similarity index 99%
rename from box_error_test.go
rename to boxerror_test.go
index 3d7f7345d..acb051d31 100644
--- a/box_error_test.go
+++ b/boxerror_test.go
@@ -8,8 +8,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/vmihailenco/msgpack/v5"
- . "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ . "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
var samples = map[string]BoxError{
diff --git a/client_tools_test.go b/client_tools_test.go
index fdd109152..fc911acf9 100644
--- a/client_tools_test.go
+++ b/client_tools_test.go
@@ -6,7 +6,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
func TestOperations_EncodeMsgpack(t *testing.T) {
diff --git a/connection.go b/connection.go
index 5f976fbf8..81172f79c 100644
--- a/connection.go
+++ b/connection.go
@@ -984,7 +984,8 @@ func (conn *Connection) newFuture(req Request) (fut *Future) {
if ctx != nil {
select {
case <-ctx.Done():
- fut.SetError(fmt.Errorf("context is done (request ID %d)", fut.requestId))
+ fut.SetError(fmt.Errorf("context is done (request ID %d) (%w)",
+ fut.requestId, ErrCancelledCtx))
shard.rmut.Unlock()
return
default:
diff --git a/crud/common.go b/crud/common.go
index 061818487..df3c4a795 100644
--- a/crud/common.go
+++ b/crud/common.go
@@ -59,7 +59,7 @@ import (
"github.com/tarantool/go-iproto"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
type baseRequest struct {
diff --git a/crud/count.go b/crud/count.go
index 9deb1e44c..b90198658 100644
--- a/crud/count.go
+++ b/crud/count.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// CountResult describes result for `crud.count` method.
@@ -73,7 +73,7 @@ type CountRequest struct {
}
type countArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Conditions []Condition
Opts CountOpts
diff --git a/crud/delete.go b/crud/delete.go
index f37da7ac5..075b25b3c 100644
--- a/crud/delete.go
+++ b/crud/delete.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// DeleteOpts describes options for `crud.delete` method.
@@ -20,7 +20,7 @@ type DeleteRequest struct {
}
type deleteArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Key Tuple
Opts DeleteOpts
diff --git a/crud/error_test.go b/crud/error_test.go
index a3db035bf..71fda30d4 100644
--- a/crud/error_test.go
+++ b/crud/error_test.go
@@ -4,7 +4,8 @@ import (
"testing"
"github.com/stretchr/testify/require"
- "github.com/tarantool/go-tarantool/v2/crud"
+
+ "github.com/tarantool/go-tarantool/v3/crud"
)
func TestErrorMany(t *testing.T) {
diff --git a/crud/example_test.go b/crud/example_test.go
index 7f9e34e0c..1b97308ae 100644
--- a/crud/example_test.go
+++ b/crud/example_test.go
@@ -6,8 +6,8 @@ import (
"reflect"
"time"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/crud"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/crud"
)
const (
@@ -65,7 +65,7 @@ func ExampleResult_rowsCustomType() {
Tuple([]interface{}{uint(2010), nil, "bla"})
type Tuple struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Id uint64
BucketId uint64
Name string
@@ -92,7 +92,7 @@ func ExampleTuples_customType() {
// The type will be encoded/decoded as an array.
type Tuple struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Id uint64
BucketId *uint64
Name string
diff --git a/crud/get.go b/crud/get.go
index 6ab91440e..5a31473ef 100644
--- a/crud/get.go
+++ b/crud/get.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// GetOpts describes options for `crud.get` method.
@@ -66,7 +66,7 @@ type GetRequest struct {
}
type getArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Key Tuple
Opts GetOpts
diff --git a/crud/insert.go b/crud/insert.go
index c696d201f..4e56c6d91 100644
--- a/crud/insert.go
+++ b/crud/insert.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// InsertOpts describes options for `crud.insert` method.
@@ -20,7 +20,7 @@ type InsertRequest struct {
}
type insertArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Tuple Tuple
Opts InsertOpts
@@ -78,7 +78,7 @@ type InsertObjectRequest struct {
}
type insertObjectArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Object Object
Opts InsertObjectOpts
diff --git a/crud/insert_many.go b/crud/insert_many.go
index 866c1ceb5..17748564d 100644
--- a/crud/insert_many.go
+++ b/crud/insert_many.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// InsertManyOpts describes options for `crud.insert_many` method.
@@ -20,7 +20,7 @@ type InsertManyRequest struct {
}
type insertManyArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Tuples Tuples
Opts InsertManyOpts
@@ -78,7 +78,7 @@ type InsertObjectManyRequest struct {
}
type insertObjectManyArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Objects Objects
Opts InsertObjectManyOpts
diff --git a/crud/len.go b/crud/len.go
index 5fef700d7..a1da72f72 100644
--- a/crud/len.go
+++ b/crud/len.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// LenResult describes result for `crud.len` method.
@@ -22,7 +22,7 @@ type LenRequest struct {
}
type lenArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Opts LenOpts
}
diff --git a/crud/max.go b/crud/max.go
index 727a17ac5..961e7724b 100644
--- a/crud/max.go
+++ b/crud/max.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// MaxOpts describes options for `crud.max` method.
@@ -20,7 +20,7 @@ type MaxRequest struct {
}
type maxArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Index interface{}
Opts MaxOpts
diff --git a/crud/min.go b/crud/min.go
index ab3bcfe07..2bbf9b816 100644
--- a/crud/min.go
+++ b/crud/min.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// MinOpts describes options for `crud.min` method.
@@ -20,7 +20,7 @@ type MinRequest struct {
}
type minArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Index interface{}
Opts MinOpts
diff --git a/crud/operations_test.go b/crud/operations_test.go
index 0ff3e818a..a7f61a8a7 100644
--- a/crud/operations_test.go
+++ b/crud/operations_test.go
@@ -6,7 +6,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2/crud"
+ "github.com/tarantool/go-tarantool/v3/crud"
)
func TestOperation_EncodeMsgpack(t *testing.T) {
diff --git a/crud/replace.go b/crud/replace.go
index 8231c9aa5..b47bba9ab 100644
--- a/crud/replace.go
+++ b/crud/replace.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// ReplaceOpts describes options for `crud.replace` method.
@@ -20,7 +20,7 @@ type ReplaceRequest struct {
}
type replaceArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Tuple Tuple
Opts ReplaceOpts
@@ -78,7 +78,7 @@ type ReplaceObjectRequest struct {
}
type replaceObjectArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Object Object
Opts ReplaceObjectOpts
diff --git a/crud/replace_many.go b/crud/replace_many.go
index 5a5143ef8..024b863b7 100644
--- a/crud/replace_many.go
+++ b/crud/replace_many.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// ReplaceManyOpts describes options for `crud.replace_many` method.
@@ -20,7 +20,7 @@ type ReplaceManyRequest struct {
}
type replaceManyArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Tuples Tuples
Opts ReplaceManyOpts
@@ -78,7 +78,7 @@ type ReplaceObjectManyRequest struct {
}
type replaceObjectManyArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Objects Objects
Opts ReplaceObjectManyOpts
diff --git a/crud/request_test.go b/crud/request_test.go
index 7c889cf40..ba2bae859 100644
--- a/crud/request_test.go
+++ b/crud/request_test.go
@@ -9,8 +9,8 @@ import (
"github.com/tarantool/go-iproto"
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/crud"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/crud"
)
const validSpace = "test" // Any valid value != default.
diff --git a/crud/result_test.go b/crud/result_test.go
index c67649f96..578eebed1 100644
--- a/crud/result_test.go
+++ b/crud/result_test.go
@@ -6,8 +6,9 @@ import (
"testing"
"github.com/stretchr/testify/require"
- "github.com/tarantool/go-tarantool/v2/crud"
"github.com/vmihailenco/msgpack/v5"
+
+ "github.com/tarantool/go-tarantool/v3/crud"
)
func TestResult_DecodeMsgpack(t *testing.T) {
diff --git a/crud/schema.go b/crud/schema.go
index 6f3b94a97..4c2d661ec 100644
--- a/crud/schema.go
+++ b/crud/schema.go
@@ -7,7 +7,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
"github.com/vmihailenco/msgpack/v5/msgpcode"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
func msgpackIsMap(code byte) bool {
diff --git a/crud/select.go b/crud/select.go
index 24dbd0cb0..b52eb7003 100644
--- a/crud/select.go
+++ b/crud/select.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// SelectOpts describes options for `crud.select` method.
@@ -90,7 +90,7 @@ type SelectRequest struct {
}
type selectArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Conditions []Condition
Opts SelectOpts
diff --git a/crud/stats.go b/crud/stats.go
index 47737f33a..c4f6988a0 100644
--- a/crud/stats.go
+++ b/crud/stats.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// StatsRequest helps you to create request object to call `crud.stats`
diff --git a/crud/storage_info.go b/crud/storage_info.go
index e2d67aadb..b39bf37a5 100644
--- a/crud/storage_info.go
+++ b/crud/storage_info.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// StatusTable describes information for instance.
@@ -98,7 +98,7 @@ type StorageInfoRequest struct {
}
type storageInfoArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Opts StorageInfoOpts
}
diff --git a/crud/tarantool_test.go b/crud/tarantool_test.go
index 8ee28cf09..0e1c1791a 100644
--- a/crud/tarantool_test.go
+++ b/crud/tarantool_test.go
@@ -10,9 +10,9 @@ import (
"github.com/stretchr/testify/require"
"github.com/tarantool/go-iproto"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/crud"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/crud"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
var server = "127.0.0.1:3013"
@@ -182,7 +182,7 @@ func connect(t testing.TB) *tarantool.Connection {
}
ret := struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Result bool
}{}
err = conn.Do(tarantool.NewCall17Request("is_ready")).GetTyped(&ret)
diff --git a/crud/truncate.go b/crud/truncate.go
index 9f80063d1..8313785d9 100644
--- a/crud/truncate.go
+++ b/crud/truncate.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// TruncateResult describes result for `crud.truncate` method.
@@ -22,7 +22,7 @@ type TruncateRequest struct {
}
type truncateArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Opts TruncateOpts
}
diff --git a/crud/update.go b/crud/update.go
index 41ebd2c09..4bdeb01ce 100644
--- a/crud/update.go
+++ b/crud/update.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// UpdateOpts describes options for `crud.update` method.
@@ -21,7 +21,7 @@ type UpdateRequest struct {
}
type updateArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Key Tuple
Operations []Operation
diff --git a/crud/upsert.go b/crud/upsert.go
index e44523d45..d55d1da1b 100644
--- a/crud/upsert.go
+++ b/crud/upsert.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// UpsertOpts describes options for `crud.upsert` method.
@@ -21,7 +21,7 @@ type UpsertRequest struct {
}
type upsertArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Tuple Tuple
Operations []Operation
@@ -90,7 +90,7 @@ type UpsertObjectRequest struct {
}
type upsertObjectArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
Object Object
Operations []Operation
diff --git a/crud/upsert_many.go b/crud/upsert_many.go
index b0ccedf09..dad7dd158 100644
--- a/crud/upsert_many.go
+++ b/crud/upsert_many.go
@@ -5,7 +5,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// UpsertManyOpts describes options for `crud.upsert_many` method.
@@ -13,7 +13,7 @@ type UpsertManyOpts = OperationManyOpts
// TupleOperationsData contains tuple with operations to be applied to tuple.
type TupleOperationsData struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Tuple Tuple
Operations []Operation
}
@@ -27,7 +27,7 @@ type UpsertManyRequest struct {
}
type upsertManyArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
TuplesOperationsData []TupleOperationsData
Opts UpsertManyOpts
@@ -79,7 +79,7 @@ type UpsertObjectManyOpts = OperationManyOpts
// ObjectOperationsData contains object with operations to be applied to object.
type ObjectOperationsData struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Object Object
Operations []Operation
}
@@ -93,7 +93,7 @@ type UpsertObjectManyRequest struct {
}
type upsertObjectManyArgs struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Space string
ObjectsOperationsData []ObjectOperationsData
Opts UpsertObjectManyOpts
diff --git a/datetime/datetime.go b/datetime/datetime.go
index f5a2a8278..23901305b 100644
--- a/datetime/datetime.go
+++ b/datetime/datetime.go
@@ -76,6 +76,7 @@ const (
const maxSize = secondsSize + nsecSize + tzIndexSize + tzOffsetSize
+//go:generate go tool gentypes -ext-code 4 Datetime
type Datetime struct {
time time.Time
}
@@ -183,6 +184,89 @@ func addMonth(ival Interval, delta int64, adjust Adjust) Interval {
return ival
}
+// MarshalMsgpack implements a custom msgpack marshaler.
+func (d Datetime) MarshalMsgpack() ([]byte, error) {
+ tm := d.ToTime()
+
+ var dt datetime
+ dt.seconds = tm.Unix()
+ dt.nsec = int32(tm.Nanosecond())
+
+ zone := tm.Location().String()
+ _, offset := tm.Zone()
+ if zone != NoTimezone {
+ // The zone value already checked in MakeDatetime() or
+ // UnmarshalMsgpack() calls.
+ dt.tzIndex = int16(timezoneToIndex[zone])
+ }
+ dt.tzOffset = int16(offset / 60)
+
+ var bytesSize = secondsSize
+ if dt.nsec != 0 || dt.tzOffset != 0 || dt.tzIndex != 0 {
+ bytesSize += nsecSize + tzIndexSize + tzOffsetSize
+ }
+
+ buf := make([]byte, bytesSize)
+ binary.LittleEndian.PutUint64(buf, uint64(dt.seconds))
+ if bytesSize == maxSize {
+ binary.LittleEndian.PutUint32(buf[secondsSize:], uint32(dt.nsec))
+ binary.LittleEndian.PutUint16(buf[secondsSize+nsecSize:], uint16(dt.tzOffset))
+ binary.LittleEndian.PutUint16(buf[secondsSize+nsecSize+tzOffsetSize:], uint16(dt.tzIndex))
+ }
+
+ return buf, nil
+}
+
+// UnmarshalMsgpack implements a custom msgpack unmarshaler.
+func (d *Datetime) UnmarshalMsgpack(data []byte) error {
+ var dt datetime
+
+ sec := binary.LittleEndian.Uint64(data)
+ dt.seconds = int64(sec)
+ dt.nsec = 0
+ if len(data) == maxSize {
+ dt.nsec = int32(binary.LittleEndian.Uint32(data[secondsSize:]))
+ dt.tzOffset = int16(binary.LittleEndian.Uint16(data[secondsSize+nsecSize:]))
+ dt.tzIndex = int16(binary.LittleEndian.Uint16(data[secondsSize+nsecSize+tzOffsetSize:]))
+ }
+
+ tt := time.Unix(dt.seconds, int64(dt.nsec))
+
+ loc := noTimezoneLoc
+ if dt.tzIndex != 0 || dt.tzOffset != 0 {
+ zone := NoTimezone
+ offset := int(dt.tzOffset) * 60
+
+ if dt.tzIndex != 0 {
+ if _, ok := indexToTimezone[int(dt.tzIndex)]; !ok {
+ return fmt.Errorf("unknown timezone index %d", dt.tzIndex)
+ }
+ zone = indexToTimezone[int(dt.tzIndex)]
+ }
+ if zone != NoTimezone {
+ if loadLoc, err := time.LoadLocation(zone); err == nil {
+ loc = loadLoc
+ } else {
+ // Unable to load location.
+ loc = time.FixedZone(zone, offset)
+ }
+ } else {
+ // Only offset.
+ loc = time.FixedZone(zone, offset)
+ }
+ }
+ tt = tt.In(loc)
+
+ newDatetime, err := MakeDatetime(tt)
+ if err != nil {
+ return err
+ }
+
+ *d = newDatetime
+
+ return nil
+}
+
func (d Datetime) add(ival Interval, positive bool) (Datetime, error) {
newVal := intervalFromDatetime(d)
@@ -244,35 +328,8 @@ func (d *Datetime) ToTime() time.Time {
func datetimeEncoder(e *msgpack.Encoder, v reflect.Value) ([]byte, error) {
dtime := v.Interface().(Datetime)
- tm := dtime.ToTime()
-
- var dt datetime
- dt.seconds = tm.Unix()
- dt.nsec = int32(tm.Nanosecond())
-
- zone := tm.Location().String()
- _, offset := tm.Zone()
- if zone != NoTimezone {
- // The zone value already checked in MakeDatetime() or
- // UnmarshalMsgpack() calls.
- dt.tzIndex = int16(timezoneToIndex[zone])
- }
- dt.tzOffset = int16(offset / 60)
- var bytesSize = secondsSize
- if dt.nsec != 0 || dt.tzOffset != 0 || dt.tzIndex != 0 {
- bytesSize += nsecSize + tzIndexSize + tzOffsetSize
- }
-
- buf := make([]byte, bytesSize)
- binary.LittleEndian.PutUint64(buf, uint64(dt.seconds))
- if bytesSize == maxSize {
- binary.LittleEndian.PutUint32(buf[secondsSize:], uint32(dt.nsec))
- binary.LittleEndian.PutUint16(buf[secondsSize+nsecSize:], uint16(dt.tzOffset))
- binary.LittleEndian.PutUint16(buf[secondsSize+nsecSize+tzOffsetSize:], uint16(dt.tzIndex))
- }
-
- return buf, nil
+ return dtime.MarshalMsgpack()
}
func datetimeDecoder(d *msgpack.Decoder, v reflect.Value, extLen int) error {
@@ -282,54 +339,15 @@ func datetimeDecoder(d *msgpack.Decoder, v reflect.Value, extLen int) error {
}
b := make([]byte, extLen)
- n, err := d.Buffered().Read(b)
- if err != nil {
+ switch n, err := d.Buffered().Read(b); {
+ case err != nil:
return err
- }
- if n < extLen {
+ case n < extLen:
return fmt.Errorf("msgpack: unexpected end of stream after %d datetime bytes", n)
}
- var dt datetime
- sec := binary.LittleEndian.Uint64(b)
- dt.seconds = int64(sec)
- dt.nsec = 0
- if extLen == maxSize {
- dt.nsec = int32(binary.LittleEndian.Uint32(b[secondsSize:]))
- dt.tzOffset = int16(binary.LittleEndian.Uint16(b[secondsSize+nsecSize:]))
- dt.tzIndex = int16(binary.LittleEndian.Uint16(b[secondsSize+nsecSize+tzOffsetSize:]))
- }
-
- tt := time.Unix(dt.seconds, int64(dt.nsec))
-
- loc := noTimezoneLoc
- if dt.tzIndex != 0 || dt.tzOffset != 0 {
- zone := NoTimezone
- offset := int(dt.tzOffset) * 60
-
- if dt.tzIndex != 0 {
- if _, ok := indexToTimezone[int(dt.tzIndex)]; !ok {
- return fmt.Errorf("unknown timezone index %d", dt.tzIndex)
- }
- zone = indexToTimezone[int(dt.tzIndex)]
- }
- if zone != NoTimezone {
- if loadLoc, err := time.LoadLocation(zone); err == nil {
- loc = loadLoc
- } else {
- // Unable to load location.
- loc = time.FixedZone(zone, offset)
- }
- } else {
- // Only offset.
- loc = time.FixedZone(zone, offset)
- }
- }
- tt = tt.In(loc)
-
ptr := v.Addr().Interface().(*Datetime)
- *ptr, err = MakeDatetime(tt)
- return err
+ return ptr.UnmarshalMsgpack(b)
}
func init() {
diff --git a/datetime/datetime_gen.go b/datetime/datetime_gen.go
new file mode 100644
index 000000000..753d9c371
--- /dev/null
+++ b/datetime/datetime_gen.go
@@ -0,0 +1,241 @@
+// Code generated by github.com/tarantool/go-option; DO NOT EDIT.
+
+package datetime
+
+import (
+ "fmt"
+
+ "github.com/vmihailenco/msgpack/v5"
+ "github.com/vmihailenco/msgpack/v5/msgpcode"
+
+ "github.com/tarantool/go-option"
+)
+
+// OptionalDatetime represents an optional value of type Datetime.
+// It can either hold a valid Datetime (IsSome == true) or be empty (IsZero == true).
+type OptionalDatetime struct {
+ value Datetime
+ exists bool
+}
+
+// SomeOptionalDatetime creates an optional OptionalDatetime with the given Datetime value.
+// The returned OptionalDatetime will have IsSome() == true and IsZero() == false.
+func SomeOptionalDatetime(value Datetime) OptionalDatetime {
+ return OptionalDatetime{
+ value: value,
+ exists: true,
+ }
+}
+
+// NoneOptionalDatetime creates an empty optional OptionalDatetime value.
+// The returned OptionalDatetime will have IsSome() == false and IsZero() == true.
+//
+// Example:
+//
+// o := NoneOptionalDatetime()
+// if o.IsZero() {
+// fmt.Println("value is absent")
+// }
+func NoneOptionalDatetime() OptionalDatetime {
+ return OptionalDatetime{}
+}
+
+func (o OptionalDatetime) newEncodeError(err error) error {
+ if err == nil {
+ return nil
+ }
+ return &option.EncodeError{
+ Type: "OptionalDatetime",
+ Parent: err,
+ }
+}
+
+func (o OptionalDatetime) newDecodeError(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ return &option.DecodeError{
+ Type: "OptionalDatetime",
+ Parent: err,
+ }
+}
+
+// IsSome returns true if the OptionalDatetime contains a value.
+// This indicates the value is explicitly set (not None).
+func (o OptionalDatetime) IsSome() bool {
+ return o.exists
+}
+
+// IsZero returns true if the OptionalDatetime does not contain a value.
+// Equivalent to !IsSome(). Useful for consistency with types where
+// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished.
+func (o OptionalDatetime) IsZero() bool {
+ return !o.exists
+}
+
+// IsNil is an alias for IsZero.
+//
+// This method is provided for compatibility with the msgpack Encoder interface.
+func (o OptionalDatetime) IsNil() bool {
+ return o.IsZero()
+}
+
+// Get returns the stored value and a boolean flag indicating its presence.
+// If the value is present, returns (value, true).
+// If the value is absent, returns (zero value of Datetime, false).
+//
+// Recommended usage:
+//
+// if value, ok := o.Get(); ok {
+// // use value
+// }
+func (o OptionalDatetime) Get() (Datetime, bool) {
+ return o.value, o.exists
+}
+
+// MustGet returns the stored value if it is present.
+// Panics if the value is absent (i.e., IsZero() == true).
+//
+// Use with caution — only when you are certain the value exists.
+//
+// Panics with: "optional value is not set" if no value is set.
+func (o OptionalDatetime) MustGet() Datetime {
+ if !o.exists {
+ panic("optional value is not set")
+ }
+
+ return o.value
+}
+
+// Unwrap returns the stored value regardless of presence.
+// If no value is set, returns the zero value for Datetime.
+//
+// Warning: Does not check presence. Use IsSome() before calling if you need
+// to distinguish between absent value and explicit zero value.
+func (o OptionalDatetime) Unwrap() Datetime {
+ return o.value
+}
+
+// UnwrapOr returns the stored value if present.
+// Otherwise, returns the provided default value.
+//
+// Example:
+//
+// o := NoneOptionalDatetime()
+// v := o.UnwrapOr(someDefaultOptionalDatetime)
+func (o OptionalDatetime) UnwrapOr(defaultValue Datetime) Datetime {
+ if o.exists {
+ return o.value
+ }
+
+ return defaultValue
+}
+
+// UnwrapOrElse returns the stored value if present.
+// Otherwise, calls the provided function and returns its result.
+// Useful when the default value requires computation or side effects.
+//
+// Example:
+//
+// o := NoneOptionalDatetime()
+// v := o.UnwrapOrElse(func() Datetime { return computeDefault() })
+func (o OptionalDatetime) UnwrapOrElse(defaultValue func() Datetime) Datetime {
+ if o.exists {
+ return o.value
+ }
+
+ return defaultValue()
+}
+
+func (o OptionalDatetime) encodeValue(encoder *msgpack.Encoder) error {
+ value, err := o.value.MarshalMsgpack()
+ if err != nil {
+ return err
+ }
+
+ err = encoder.EncodeExtHeader(4, len(value))
+ if err != nil {
+ return err
+ }
+
+ _, err = encoder.Writer().Write(value)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// EncodeMsgpack encodes the OptionalDatetime value using MessagePack format.
+// - If the value is present, it is encoded as Datetime.
+// - If the value is absent (None), it is encoded as nil.
+//
+// Returns an error if encoding fails.
+func (o OptionalDatetime) EncodeMsgpack(encoder *msgpack.Encoder) error {
+ if o.exists {
+ return o.newEncodeError(o.encodeValue(encoder))
+ }
+
+ return o.newEncodeError(encoder.EncodeNil())
+}
+
+func (o *OptionalDatetime) decodeValue(decoder *msgpack.Decoder) error {
+ tp, length, err := decoder.DecodeExtHeader()
+ switch {
+ case err != nil:
+ return o.newDecodeError(err)
+ case tp != 4:
+ return o.newDecodeError(fmt.Errorf("invalid extension code: %d", tp))
+ }
+
+ a := make([]byte, length)
+ if err := decoder.ReadFull(a); err != nil {
+ return o.newDecodeError(err)
+ }
+
+ if err := o.value.UnmarshalMsgpack(a); err != nil {
+ return o.newDecodeError(err)
+ }
+
+ o.exists = true
+ return nil
+}
+
+func (o *OptionalDatetime) checkCode(code byte) bool {
+ return msgpcode.IsExt(code)
+}
+
+// DecodeMsgpack decodes a OptionalDatetime value from MessagePack format.
+// Supports two input types:
+// - nil: interpreted as no value (NoneOptionalDatetime)
+// - Datetime: interpreted as a present value (SomeOptionalDatetime)
+//
+// Returns an error if the input type is unsupported or decoding fails.
+//
+// After successful decoding:
+// - on nil: exists = false, value = default zero value
+// - on Datetime: exists = true, value = decoded value
+func (o *OptionalDatetime) DecodeMsgpack(decoder *msgpack.Decoder) error {
+ code, err := decoder.PeekCode()
+ if err != nil {
+ return o.newDecodeError(err)
+ }
+
+ switch {
+ case code == msgpcode.Nil:
+ o.exists = false
+
+ return o.newDecodeError(decoder.Skip())
+ case o.checkCode(code):
+ err := o.decodeValue(decoder)
+ if err != nil {
+ return o.newDecodeError(err)
+ }
+ o.exists = true
+
+ return err
+ default:
+ return o.newDecodeError(fmt.Errorf("unexpected code: %d", code))
+ }
+}
diff --git a/datetime/datetime_gen_test.go b/datetime/datetime_gen_test.go
new file mode 100644
index 000000000..6b45b6fb1
--- /dev/null
+++ b/datetime/datetime_gen_test.go
@@ -0,0 +1,125 @@
+package datetime
+
+import (
+ "bytes"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/vmihailenco/msgpack/v5"
+)
+
+func TestSomeOptionalDatetime(t *testing.T) {
+ val, err := MakeDatetime(time.Now().In(time.UTC))
+ assert.NoError(t, err)
+ opt := SomeOptionalDatetime(val)
+
+ assert.True(t, opt.IsSome())
+ assert.False(t, opt.IsZero())
+
+ v, ok := opt.Get()
+ assert.True(t, ok)
+ assert.Equal(t, val, v)
+}
+
+func TestNoneOptionalDatetime(t *testing.T) {
+ opt := NoneOptionalDatetime()
+
+ assert.False(t, opt.IsSome())
+ assert.True(t, opt.IsZero())
+
+ _, ok := opt.Get()
+ assert.False(t, ok)
+}
+
+func TestOptionalDatetime_MustGet(t *testing.T) {
+ val, err := MakeDatetime(time.Now().In(time.UTC))
+ assert.NoError(t, err)
+ optSome := SomeOptionalDatetime(val)
+ optNone := NoneOptionalDatetime()
+
+ assert.Equal(t, val, optSome.MustGet())
+ assert.Panics(t, func() { optNone.MustGet() })
+}
+
+func TestOptionalDatetime_Unwrap(t *testing.T) {
+ val, err := MakeDatetime(time.Now().In(time.UTC))
+ assert.NoError(t, err)
+ optSome := SomeOptionalDatetime(val)
+ optNone := NoneOptionalDatetime()
+
+ assert.Equal(t, val, optSome.Unwrap())
+ assert.Equal(t, Datetime{}, optNone.Unwrap())
+}
+
+func TestOptionalDatetime_UnwrapOr(t *testing.T) {
+ val, err := MakeDatetime(time.Now().In(time.UTC))
+ assert.NoError(t, err)
+ def, err := MakeDatetime(time.Now().Add(1 * time.Hour).In(time.UTC))
+ assert.NoError(t, err)
+ optSome := SomeOptionalDatetime(val)
+ optNone := NoneOptionalDatetime()
+
+ assert.Equal(t, val, optSome.UnwrapOr(def))
+ assert.Equal(t, def, optNone.UnwrapOr(def))
+}
+
+func TestOptionalDatetime_UnwrapOrElse(t *testing.T) {
+ val, err := MakeDatetime(time.Now().In(time.UTC))
+ assert.NoError(t, err)
+ def, err := MakeDatetime(time.Now().Add(1 * time.Hour).In(time.UTC))
+ assert.NoError(t, err)
+ optSome := SomeOptionalDatetime(val)
+ optNone := NoneOptionalDatetime()
+
+ assert.Equal(t, val, optSome.UnwrapOrElse(func() Datetime { return def }))
+ assert.Equal(t, def, optNone.UnwrapOrElse(func() Datetime { return def }))
+}
+
+func TestOptionalDatetime_EncodeDecodeMsgpack_Some(t *testing.T) {
+ val, err := MakeDatetime(time.Now().In(time.UTC))
+ assert.NoError(t, err)
+ some := SomeOptionalDatetime(val)
+
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err = enc.Encode(some)
+ assert.NoError(t, err)
+
+ var decodedSome OptionalDatetime
+ err = dec.Decode(&decodedSome)
+ assert.NoError(t, err)
+ assert.True(t, decodedSome.IsSome())
+ assert.Equal(t, val, decodedSome.Unwrap())
+}
+
+func TestOptionalDatetime_EncodeDecodeMsgpack_None(t *testing.T) {
+ none := NoneOptionalDatetime()
+
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(none)
+ assert.NoError(t, err)
+
+ var decodedNone OptionalDatetime
+ err = dec.Decode(&decodedNone)
+ assert.NoError(t, err)
+ assert.True(t, decodedNone.IsZero())
+}
+
+func TestOptionalDatetime_EncodeDecodeMsgpack_InvalidType(t *testing.T) {
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(123)
+ assert.NoError(t, err)
+
+ var decodedInvalid OptionalDatetime
+ err = dec.Decode(&decodedInvalid)
+ assert.Error(t, err)
+}
diff --git a/datetime/datetime_test.go b/datetime/datetime_test.go
index 8be50e0e7..d01153892 100644
--- a/datetime/datetime_test.go
+++ b/datetime/datetime_test.go
@@ -11,9 +11,9 @@ import (
"github.com/vmihailenco/msgpack/v5"
- . "github.com/tarantool/go-tarantool/v2"
- . "github.com/tarantool/go-tarantool/v2/datetime"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ . "github.com/tarantool/go-tarantool/v3"
+ . "github.com/tarantool/go-tarantool/v3/datetime"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
var noTimezoneLoc = time.FixedZone(NoTimezone, 0)
diff --git a/datetime/example_test.go b/datetime/example_test.go
index ac5f40500..df5d55563 100644
--- a/datetime/example_test.go
+++ b/datetime/example_test.go
@@ -13,8 +13,8 @@ import (
"fmt"
"time"
- "github.com/tarantool/go-tarantool/v2"
- . "github.com/tarantool/go-tarantool/v2/datetime"
+ "github.com/tarantool/go-tarantool/v3"
+ . "github.com/tarantool/go-tarantool/v3/datetime"
)
// Example demonstrates how to use tuples with datetime. To enable support of
diff --git a/datetime/interval.go b/datetime/interval.go
index bcd052383..e6d39e4d8 100644
--- a/datetime/interval.go
+++ b/datetime/interval.go
@@ -2,7 +2,6 @@ package datetime
import (
"bytes"
- "fmt"
"reflect"
"github.com/vmihailenco/msgpack/v5"
@@ -23,6 +22,8 @@ const (
)
// Interval type is GoLang implementation of Tarantool intervals.
+//
+//go:generate go tool gentypes -ext-code 6 Interval
type Interval struct {
Year int64
Month int64
@@ -35,6 +36,21 @@ type Interval struct {
Adjust Adjust
}
+func (ival Interval) countNonZeroFields() int {
+ count := 0
+
+ for _, field := range []int64{
+ ival.Year, ival.Month, ival.Week, ival.Day, ival.Hour,
+ ival.Min, ival.Sec, ival.Nsec, adjustToDt[ival.Adjust],
+ } {
+ if field != 0 {
+ count++
+ }
+ }
+
+ return count
+}
+
// We use int64 for every field to avoid changes in the future, see:
// https://github.com/tarantool/tarantool/blob/943ce3caf8401510ced4f074bca7006c3d73f9b3/src/lib/core/datetime.h#L106
@@ -66,115 +82,134 @@ func (ival Interval) Sub(sub Interval) Interval {
return ival
}
-func encodeIntervalValue(e *msgpack.Encoder, typ uint64, value int64) (err error) {
- if value == 0 {
- return
- }
- err = e.EncodeUint(typ)
- if err == nil {
- if value > 0 {
- err = e.EncodeUint(uint64(value))
- } else if value < 0 {
- err = e.EncodeInt(value)
- }
+// MarshalMsgpack implements a custom msgpack marshaler.
+func (ival Interval) MarshalMsgpack() ([]byte, error) {
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+
+ if err := ival.MarshalMsgpackTo(enc); err != nil {
+ return nil, err
}
- return
-}
-func encodeInterval(e *msgpack.Encoder, v reflect.Value) (err error) {
- val := v.Interface().(Interval)
+ return buf.Bytes(), nil
+}
- var fieldNum uint64
- for _, val := range []int64{val.Year, val.Month, val.Week, val.Day,
- val.Hour, val.Min, val.Sec, val.Nsec,
- adjustToDt[val.Adjust]} {
- if val != 0 {
- fieldNum++
- }
- }
- if err = e.EncodeUint(fieldNum); err != nil {
- return
+// MarshalMsgpackTo implements a custom msgpack marshaler.
+func (ival Interval) MarshalMsgpackTo(e *msgpack.Encoder) error {
+ var fieldNum = uint64(ival.countNonZeroFields())
+ if err := e.EncodeUint(fieldNum); err != nil {
+ return err
}
- if err = encodeIntervalValue(e, fieldYear, val.Year); err != nil {
- return
+ if err := encodeIntervalValue(e, fieldYear, ival.Year); err != nil {
+ return err
}
- if err = encodeIntervalValue(e, fieldMonth, val.Month); err != nil {
- return
+ if err := encodeIntervalValue(e, fieldMonth, ival.Month); err != nil {
+ return err
}
- if err = encodeIntervalValue(e, fieldWeek, val.Week); err != nil {
- return
+ if err := encodeIntervalValue(e, fieldWeek, ival.Week); err != nil {
+ return err
}
- if err = encodeIntervalValue(e, fieldDay, val.Day); err != nil {
- return
+ if err := encodeIntervalValue(e, fieldDay, ival.Day); err != nil {
+ return err
}
- if err = encodeIntervalValue(e, fieldHour, val.Hour); err != nil {
- return
+ if err := encodeIntervalValue(e, fieldHour, ival.Hour); err != nil {
+ return err
}
- if err = encodeIntervalValue(e, fieldMin, val.Min); err != nil {
- return
+ if err := encodeIntervalValue(e, fieldMin, ival.Min); err != nil {
+ return err
}
- if err = encodeIntervalValue(e, fieldSec, val.Sec); err != nil {
- return
+ if err := encodeIntervalValue(e, fieldSec, ival.Sec); err != nil {
+ return err
}
- if err = encodeIntervalValue(e, fieldNSec, val.Nsec); err != nil {
- return
+ if err := encodeIntervalValue(e, fieldNSec, ival.Nsec); err != nil {
+ return err
}
- if err = encodeIntervalValue(e, fieldAdjust, adjustToDt[val.Adjust]); err != nil {
- return
+ if err := encodeIntervalValue(e, fieldAdjust, adjustToDt[ival.Adjust]); err != nil {
+ return err
}
+
return nil
}
-func decodeInterval(d *msgpack.Decoder, v reflect.Value) (err error) {
- var fieldNum uint
- if fieldNum, err = d.DecodeUint(); err != nil {
- return
+// UnmarshalMsgpackFrom implements a custom msgpack unmarshaler.
+func (ival *Interval) UnmarshalMsgpackFrom(d *msgpack.Decoder) error {
+ fieldNum, err := d.DecodeUint()
+ if err != nil {
+ return err
}
- var val Interval
+ ival.Adjust = dtToAdjust[int64(NoneAdjust)]
- hasAdjust := false
for i := 0; i < int(fieldNum); i++ {
var fieldType uint
if fieldType, err = d.DecodeUint(); err != nil {
- return
+ return err
}
+
var fieldVal int64
if fieldVal, err = d.DecodeInt64(); err != nil {
- return
+ return err
}
+
switch fieldType {
case fieldYear:
- val.Year = fieldVal
+ ival.Year = fieldVal
case fieldMonth:
- val.Month = fieldVal
+ ival.Month = fieldVal
case fieldWeek:
- val.Week = fieldVal
+ ival.Week = fieldVal
case fieldDay:
- val.Day = fieldVal
+ ival.Day = fieldVal
case fieldHour:
- val.Hour = fieldVal
+ ival.Hour = fieldVal
case fieldMin:
- val.Min = fieldVal
+ ival.Min = fieldVal
case fieldSec:
- val.Sec = fieldVal
+ ival.Sec = fieldVal
case fieldNSec:
- val.Nsec = fieldVal
+ ival.Nsec = fieldVal
case fieldAdjust:
- hasAdjust = true
- if adjust, ok := dtToAdjust[fieldVal]; ok {
- val.Adjust = adjust
- } else {
- return fmt.Errorf("unsupported Adjust: %d", fieldVal)
- }
- default:
- return fmt.Errorf("unsupported interval field type: %d", fieldType)
+ ival.Adjust = dtToAdjust[fieldVal]
}
}
- if !hasAdjust {
- val.Adjust = dtToAdjust[0]
+ return nil
+}
+
+// UnmarshalMsgpack implements a custom msgpack unmarshaler.
+func (ival *Interval) UnmarshalMsgpack(data []byte) error {
+ dec := msgpack.NewDecoder(bytes.NewReader(data))
+ return ival.UnmarshalMsgpackFrom(dec)
+}
+
+func encodeIntervalValue(e *msgpack.Encoder, typ uint64, value int64) error {
+ if value == 0 {
+ return nil
+ }
+
+ err := e.EncodeUint(typ)
+ if err != nil {
+ return err
+ }
+
+ switch {
+ case value > 0:
+ return e.EncodeUint(uint64(value))
+ default:
+ return e.EncodeInt(value)
+ }
+}
+
+func encodeInterval(e *msgpack.Encoder, v reflect.Value) (err error) {
+ val := v.Interface().(Interval)
+ return val.MarshalMsgpackTo(e)
+}
+
+func decodeInterval(d *msgpack.Decoder, v reflect.Value) (err error) {
+ val := Interval{}
+ if err = val.UnmarshalMsgpackFrom(d); err != nil {
+ return
}
v.Set(reflect.ValueOf(val))
diff --git a/datetime/interval_gen.go b/datetime/interval_gen.go
new file mode 100644
index 000000000..2cccaaca0
--- /dev/null
+++ b/datetime/interval_gen.go
@@ -0,0 +1,241 @@
+// Code generated by github.com/tarantool/go-option; DO NOT EDIT.
+
+package datetime
+
+import (
+ "fmt"
+
+ "github.com/vmihailenco/msgpack/v5"
+ "github.com/vmihailenco/msgpack/v5/msgpcode"
+
+ "github.com/tarantool/go-option"
+)
+
+// OptionalInterval represents an optional value of type Interval.
+// It can either hold a valid Interval (IsSome == true) or be empty (IsZero == true).
+type OptionalInterval struct {
+ value Interval
+ exists bool
+}
+
+// SomeOptionalInterval creates an optional OptionalInterval with the given Interval value.
+// The returned OptionalInterval will have IsSome() == true and IsZero() == false.
+func SomeOptionalInterval(value Interval) OptionalInterval {
+ return OptionalInterval{
+ value: value,
+ exists: true,
+ }
+}
+
+// NoneOptionalInterval creates an empty optional OptionalInterval value.
+// The returned OptionalInterval will have IsSome() == false and IsZero() == true.
+//
+// Example:
+//
+// o := NoneOptionalInterval()
+// if o.IsZero() {
+// fmt.Println("value is absent")
+// }
+func NoneOptionalInterval() OptionalInterval {
+ return OptionalInterval{}
+}
+
+func (o OptionalInterval) newEncodeError(err error) error {
+ if err == nil {
+ return nil
+ }
+ return &option.EncodeError{
+ Type: "OptionalInterval",
+ Parent: err,
+ }
+}
+
+func (o OptionalInterval) newDecodeError(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ return &option.DecodeError{
+ Type: "OptionalInterval",
+ Parent: err,
+ }
+}
+
+// IsSome returns true if the OptionalInterval contains a value.
+// This indicates the value is explicitly set (not None).
+func (o OptionalInterval) IsSome() bool {
+ return o.exists
+}
+
+// IsZero returns true if the OptionalInterval does not contain a value.
+// Equivalent to !IsSome(). Useful for consistency with types where
+// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished.
+func (o OptionalInterval) IsZero() bool {
+ return !o.exists
+}
+
+// IsNil is an alias for IsZero.
+//
+// This method is provided for compatibility with the msgpack Encoder interface.
+func (o OptionalInterval) IsNil() bool {
+ return o.IsZero()
+}
+
+// Get returns the stored value and a boolean flag indicating its presence.
+// If the value is present, returns (value, true).
+// If the value is absent, returns (zero value of Interval, false).
+//
+// Recommended usage:
+//
+// if value, ok := o.Get(); ok {
+// // use value
+// }
+func (o OptionalInterval) Get() (Interval, bool) {
+ return o.value, o.exists
+}
+
+// MustGet returns the stored value if it is present.
+// Panics if the value is absent (i.e., IsZero() == true).
+//
+// Use with caution — only when you are certain the value exists.
+//
+// Panics with: "optional value is not set" if no value is set.
+func (o OptionalInterval) MustGet() Interval {
+ if !o.exists {
+ panic("optional value is not set")
+ }
+
+ return o.value
+}
+
+// Unwrap returns the stored value regardless of presence.
+// If no value is set, returns the zero value for Interval.
+//
+// Warning: Does not check presence. Use IsSome() before calling if you need
+// to distinguish between absent value and explicit zero value.
+func (o OptionalInterval) Unwrap() Interval {
+ return o.value
+}
+
+// UnwrapOr returns the stored value if present.
+// Otherwise, returns the provided default value.
+//
+// Example:
+//
+// o := NoneOptionalInterval()
+// v := o.UnwrapOr(someDefaultOptionalInterval)
+func (o OptionalInterval) UnwrapOr(defaultValue Interval) Interval {
+ if o.exists {
+ return o.value
+ }
+
+ return defaultValue
+}
+
+// UnwrapOrElse returns the stored value if present.
+// Otherwise, calls the provided function and returns its result.
+// Useful when the default value requires computation or side effects.
+//
+// Example:
+//
+// o := NoneOptionalInterval()
+// v := o.UnwrapOrElse(func() Interval { return computeDefault() })
+func (o OptionalInterval) UnwrapOrElse(defaultValue func() Interval) Interval {
+ if o.exists {
+ return o.value
+ }
+
+ return defaultValue()
+}
+
+func (o OptionalInterval) encodeValue(encoder *msgpack.Encoder) error {
+ value, err := o.value.MarshalMsgpack()
+ if err != nil {
+ return err
+ }
+
+ err = encoder.EncodeExtHeader(6, len(value))
+ if err != nil {
+ return err
+ }
+
+ _, err = encoder.Writer().Write(value)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// EncodeMsgpack encodes the OptionalInterval value using MessagePack format.
+// - If the value is present, it is encoded as Interval.
+// - If the value is absent (None), it is encoded as nil.
+//
+// Returns an error if encoding fails.
+func (o OptionalInterval) EncodeMsgpack(encoder *msgpack.Encoder) error {
+ if o.exists {
+ return o.newEncodeError(o.encodeValue(encoder))
+ }
+
+ return o.newEncodeError(encoder.EncodeNil())
+}
+
+func (o *OptionalInterval) decodeValue(decoder *msgpack.Decoder) error {
+ tp, length, err := decoder.DecodeExtHeader()
+ switch {
+ case err != nil:
+ return o.newDecodeError(err)
+ case tp != 6:
+ return o.newDecodeError(fmt.Errorf("invalid extension code: %d", tp))
+ }
+
+ a := make([]byte, length)
+ if err := decoder.ReadFull(a); err != nil {
+ return o.newDecodeError(err)
+ }
+
+ if err := o.value.UnmarshalMsgpack(a); err != nil {
+ return o.newDecodeError(err)
+ }
+
+ o.exists = true
+ return nil
+}
+
+func (o *OptionalInterval) checkCode(code byte) bool {
+ return msgpcode.IsExt(code)
+}
+
+// DecodeMsgpack decodes a OptionalInterval value from MessagePack format.
+// Supports two input types:
+// - nil: interpreted as no value (NoneOptionalInterval)
+// - Interval: interpreted as a present value (SomeOptionalInterval)
+//
+// Returns an error if the input type is unsupported or decoding fails.
+//
+// After successful decoding:
+// - on nil: exists = false, value = default zero value
+// - on Interval: exists = true, value = decoded value
+func (o *OptionalInterval) DecodeMsgpack(decoder *msgpack.Decoder) error {
+ code, err := decoder.PeekCode()
+ if err != nil {
+ return o.newDecodeError(err)
+ }
+
+ switch {
+ case code == msgpcode.Nil:
+ o.exists = false
+
+ return o.newDecodeError(decoder.Skip())
+ case o.checkCode(code):
+ err := o.decodeValue(decoder)
+ if err != nil {
+ return o.newDecodeError(err)
+ }
+ o.exists = true
+
+ return err
+ default:
+ return o.newDecodeError(fmt.Errorf("unexpected code: %d", code))
+ }
+}
diff --git a/datetime/interval_gen_test.go b/datetime/interval_gen_test.go
new file mode 100644
index 000000000..162db3336
--- /dev/null
+++ b/datetime/interval_gen_test.go
@@ -0,0 +1,116 @@
+package datetime
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/vmihailenco/msgpack/v5"
+)
+
+func TestSomeOptionalInterval(t *testing.T) {
+ val := Interval{Year: 1}
+ opt := SomeOptionalInterval(val)
+
+ assert.True(t, opt.IsSome())
+ assert.False(t, opt.IsZero())
+
+ v, ok := opt.Get()
+ assert.True(t, ok)
+ assert.Equal(t, val, v)
+}
+
+func TestNoneOptionalInterval(t *testing.T) {
+ opt := NoneOptionalInterval()
+
+ assert.False(t, opt.IsSome())
+ assert.True(t, opt.IsZero())
+
+ _, ok := opt.Get()
+ assert.False(t, ok)
+}
+
+func TestOptionalInterval_MustGet(t *testing.T) {
+ val := Interval{Year: 1}
+ optSome := SomeOptionalInterval(val)
+ optNone := NoneOptionalInterval()
+
+ assert.Equal(t, val, optSome.MustGet())
+ assert.Panics(t, func() { optNone.MustGet() })
+}
+
+func TestOptionalInterval_Unwrap(t *testing.T) {
+ val := Interval{Year: 1}
+ optSome := SomeOptionalInterval(val)
+ optNone := NoneOptionalInterval()
+
+ assert.Equal(t, val, optSome.Unwrap())
+ assert.Equal(t, Interval{}, optNone.Unwrap())
+}
+
+func TestOptionalInterval_UnwrapOr(t *testing.T) {
+ val := Interval{Year: 1}
+ def := Interval{Year: 2}
+ optSome := SomeOptionalInterval(val)
+ optNone := NoneOptionalInterval()
+
+ assert.Equal(t, val, optSome.UnwrapOr(def))
+ assert.Equal(t, def, optNone.UnwrapOr(def))
+}
+
+func TestOptionalInterval_UnwrapOrElse(t *testing.T) {
+ val := Interval{Year: 1}
+ def := Interval{Year: 2}
+ optSome := SomeOptionalInterval(val)
+ optNone := NoneOptionalInterval()
+
+ assert.Equal(t, val, optSome.UnwrapOrElse(func() Interval { return def }))
+ assert.Equal(t, def, optNone.UnwrapOrElse(func() Interval { return def }))
+}
+
+func TestOptionalInterval_EncodeDecodeMsgpack_Some(t *testing.T) {
+ val := Interval{Year: 1}
+ some := SomeOptionalInterval(val)
+
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(some)
+ assert.NoError(t, err)
+
+ var decodedSome OptionalInterval
+ err = dec.Decode(&decodedSome)
+ assert.NoError(t, err)
+ assert.True(t, decodedSome.IsSome())
+ assert.Equal(t, val, decodedSome.Unwrap())
+}
+
+func TestOptionalInterval_EncodeDecodeMsgpack_None(t *testing.T) {
+ none := NoneOptionalInterval()
+
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(none)
+ assert.NoError(t, err)
+
+ var decodedNone OptionalInterval
+ err = dec.Decode(&decodedNone)
+ assert.NoError(t, err)
+ assert.True(t, decodedNone.IsZero())
+}
+
+func TestOptionalInterval_EncodeDecodeMsgpack_InvalidType(t *testing.T) {
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(123)
+ assert.NoError(t, err)
+
+ var decodedInvalid OptionalInterval
+ err = dec.Decode(&decodedInvalid)
+ assert.Error(t, err)
+}
diff --git a/datetime/interval_test.go b/datetime/interval_test.go
index 95142fe47..2f4bb8a66 100644
--- a/datetime/interval_test.go
+++ b/datetime/interval_test.go
@@ -5,9 +5,9 @@ import (
"reflect"
"testing"
- "github.com/tarantool/go-tarantool/v2"
- . "github.com/tarantool/go-tarantool/v2/datetime"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ "github.com/tarantool/go-tarantool/v3"
+ . "github.com/tarantool/go-tarantool/v3/datetime"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
func TestIntervalAdd(t *testing.T) {
diff --git a/decimal/decimal.go b/decimal/decimal.go
index 3a1abb76e..3c2681238 100644
--- a/decimal/decimal.go
+++ b/decimal/decimal.go
@@ -44,13 +44,14 @@ const (
)
var (
- one decimal.Decimal = decimal.NewFromInt(1)
+ one = decimal.NewFromInt(1)
// -10^decimalPrecision - 1
- minSupportedDecimal decimal.Decimal = maxSupportedDecimal.Neg().Sub(one)
+ minSupportedDecimal = maxSupportedDecimal.Neg().Sub(one)
// 10^decimalPrecision - 1
- maxSupportedDecimal decimal.Decimal = decimal.New(1, decimalPrecision).Sub(one)
+ maxSupportedDecimal = decimal.New(1, decimalPrecision).Sub(one)
)
+//go:generate go tool gentypes -ext-code 1 Decimal
type Decimal struct {
decimal.Decimal
}
@@ -71,37 +72,20 @@ func MakeDecimalFromString(src string) (Decimal, error) {
return result, nil
}
-func decimalEncoder(e *msgpack.Encoder, v reflect.Value) ([]byte, error) {
- dec := v.Interface().(Decimal)
- if dec.GreaterThan(maxSupportedDecimal) {
- return nil,
- fmt.Errorf(
- "msgpack: decimal number is bigger than maximum supported number (10^%d - 1)",
- decimalPrecision)
- }
- if dec.LessThan(minSupportedDecimal) {
- return nil,
- fmt.Errorf(
- "msgpack: decimal number is lesser than minimum supported number (-10^%d - 1)",
- decimalPrecision)
- }
-
- strBuf := dec.String()
- bcdBuf, err := encodeStringToBCD(strBuf)
- if err != nil {
- return nil, fmt.Errorf("msgpack: can't encode string (%s) to a BCD buffer: %w", strBuf, err)
- }
- return bcdBuf, nil
-}
+var (
+ ErrDecimalOverflow = fmt.Errorf("msgpack: decimal number is bigger than"+
+ " maximum supported number (10^%d - 1)", decimalPrecision)
+ ErrDecimalUnderflow = fmt.Errorf("msgpack: decimal number is lesser than"+
+ " minimum supported number (-10^%d - 1)", decimalPrecision)
+)
-func decimalDecoder(d *msgpack.Decoder, v reflect.Value, extLen int) error {
- b := make([]byte, extLen)
- n, err := d.Buffered().Read(b)
- if err != nil {
- return err
- }
- if n < extLen {
- return fmt.Errorf("msgpack: unexpected end of stream after %d decimal bytes", n)
+// MarshalMsgpack implements a custom msgpack marshaler.
+func (d Decimal) MarshalMsgpack() ([]byte, error) {
+ switch {
+ case d.GreaterThan(maxSupportedDecimal):
+ return nil, ErrDecimalOverflow
+ case d.LessThan(minSupportedDecimal):
+ return nil, ErrDecimalUnderflow
}
// Decimal values can be encoded to fixext MessagePack, where buffer
@@ -112,9 +96,19 @@ func decimalDecoder(d *msgpack.Decoder, v reflect.Value, extLen int) error {
// +--------+-------------------+------------+===============+
// | MP_EXT | length (optional) | MP_DECIMAL | PackedDecimal |
// +--------+-------------------+------------+===============+
- digits, exp, err := decodeStringFromBCD(b)
+ strBuf := d.String()
+ bcdBuf, err := encodeStringToBCD(strBuf)
if err != nil {
- return fmt.Errorf("msgpack: can't decode string from BCD buffer (%x): %w", b, err)
+ return nil, fmt.Errorf("msgpack: can't encode string (%s) to a BCD buffer: %w", strBuf, err)
+ }
+ return bcdBuf, nil
+}
+
+// UnmarshalMsgpack implements a custom msgpack unmarshaler.
+func (d *Decimal) UnmarshalMsgpack(data []byte) error {
+ digits, exp, err := decodeStringFromBCD(data)
+ if err != nil {
+ return fmt.Errorf("msgpack: can't decode string from BCD buffer (%x): %w", data, err)
}
dec, err := decimal.NewFromString(digits)
@@ -125,11 +119,31 @@ func decimalDecoder(d *msgpack.Decoder, v reflect.Value, extLen int) error {
if exp != 0 {
dec = dec.Shift(int32(exp))
}
- ptr := v.Addr().Interface().(*Decimal)
- *ptr = MakeDecimal(dec)
+
+ *d = MakeDecimal(dec)
return nil
}
+func decimalEncoder(e *msgpack.Encoder, v reflect.Value) ([]byte, error) {
+ dec := v.Interface().(Decimal)
+
+ return dec.MarshalMsgpack()
+}
+
+func decimalDecoder(d *msgpack.Decoder, v reflect.Value, extLen int) error {
+ b := make([]byte, extLen)
+
+ switch n, err := d.Buffered().Read(b); {
+ case err != nil:
+ return err
+ case n < extLen:
+ return fmt.Errorf("msgpack: unexpected end of stream after %d decimal bytes", n)
+ }
+
+ ptr := v.Addr().Interface().(*Decimal)
+ return ptr.UnmarshalMsgpack(b)
+}
+
func init() {
msgpack.RegisterExtDecoder(decimalExtID, Decimal{}, decimalDecoder)
msgpack.RegisterExtEncoder(decimalExtID, Decimal{}, decimalEncoder)
diff --git a/decimal/decimal_gen.go b/decimal/decimal_gen.go
new file mode 100644
index 000000000..0f9b18e3a
--- /dev/null
+++ b/decimal/decimal_gen.go
@@ -0,0 +1,241 @@
+// Code generated by github.com/tarantool/go-option; DO NOT EDIT.
+
+package decimal
+
+import (
+ "fmt"
+
+ "github.com/vmihailenco/msgpack/v5"
+ "github.com/vmihailenco/msgpack/v5/msgpcode"
+
+ "github.com/tarantool/go-option"
+)
+
+// OptionalDecimal represents an optional value of type Decimal.
+// It can either hold a valid Decimal (IsSome == true) or be empty (IsZero == true).
+type OptionalDecimal struct {
+ value Decimal
+ exists bool
+}
+
+// SomeOptionalDecimal creates an optional OptionalDecimal with the given Decimal value.
+// The returned OptionalDecimal will have IsSome() == true and IsZero() == false.
+func SomeOptionalDecimal(value Decimal) OptionalDecimal {
+ return OptionalDecimal{
+ value: value,
+ exists: true,
+ }
+}
+
+// NoneOptionalDecimal creates an empty optional OptionalDecimal value.
+// The returned OptionalDecimal will have IsSome() == false and IsZero() == true.
+//
+// Example:
+//
+// o := NoneOptionalDecimal()
+// if o.IsZero() {
+// fmt.Println("value is absent")
+// }
+func NoneOptionalDecimal() OptionalDecimal {
+ return OptionalDecimal{}
+}
+
+func (o OptionalDecimal) newEncodeError(err error) error {
+ if err == nil {
+ return nil
+ }
+ return &option.EncodeError{
+ Type: "OptionalDecimal",
+ Parent: err,
+ }
+}
+
+func (o OptionalDecimal) newDecodeError(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ return &option.DecodeError{
+ Type: "OptionalDecimal",
+ Parent: err,
+ }
+}
+
+// IsSome returns true if the OptionalDecimal contains a value.
+// This indicates the value is explicitly set (not None).
+func (o OptionalDecimal) IsSome() bool {
+ return o.exists
+}
+
+// IsZero returns true if the OptionalDecimal does not contain a value.
+// Equivalent to !IsSome(). Useful for consistency with types where
+// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished.
+func (o OptionalDecimal) IsZero() bool {
+ return !o.exists
+}
+
+// IsNil is an alias for IsZero.
+//
+// This method is provided for compatibility with the msgpack Encoder interface.
+func (o OptionalDecimal) IsNil() bool {
+ return o.IsZero()
+}
+
+// Get returns the stored value and a boolean flag indicating its presence.
+// If the value is present, returns (value, true).
+// If the value is absent, returns (zero value of Decimal, false).
+//
+// Recommended usage:
+//
+// if value, ok := o.Get(); ok {
+// // use value
+// }
+func (o OptionalDecimal) Get() (Decimal, bool) {
+ return o.value, o.exists
+}
+
+// MustGet returns the stored value if it is present.
+// Panics if the value is absent (i.e., IsZero() == true).
+//
+// Use with caution — only when you are certain the value exists.
+//
+// Panics with: "optional value is not set" if no value is set.
+func (o OptionalDecimal) MustGet() Decimal {
+ if !o.exists {
+ panic("optional value is not set")
+ }
+
+ return o.value
+}
+
+// Unwrap returns the stored value regardless of presence.
+// If no value is set, returns the zero value for Decimal.
+//
+// Warning: Does not check presence. Use IsSome() before calling if you need
+// to distinguish between absent value and explicit zero value.
+func (o OptionalDecimal) Unwrap() Decimal {
+ return o.value
+}
+
+// UnwrapOr returns the stored value if present.
+// Otherwise, returns the provided default value.
+//
+// Example:
+//
+// o := NoneOptionalDecimal()
+// v := o.UnwrapOr(someDefaultOptionalDecimal)
+func (o OptionalDecimal) UnwrapOr(defaultValue Decimal) Decimal {
+ if o.exists {
+ return o.value
+ }
+
+ return defaultValue
+}
+
+// UnwrapOrElse returns the stored value if present.
+// Otherwise, calls the provided function and returns its result.
+// Useful when the default value requires computation or side effects.
+//
+// Example:
+//
+// o := NoneOptionalDecimal()
+// v := o.UnwrapOrElse(func() Decimal { return computeDefault() })
+func (o OptionalDecimal) UnwrapOrElse(defaultValue func() Decimal) Decimal {
+ if o.exists {
+ return o.value
+ }
+
+ return defaultValue()
+}
+
+func (o OptionalDecimal) encodeValue(encoder *msgpack.Encoder) error {
+ value, err := o.value.MarshalMsgpack()
+ if err != nil {
+ return err
+ }
+
+ err = encoder.EncodeExtHeader(1, len(value))
+ if err != nil {
+ return err
+ }
+
+ _, err = encoder.Writer().Write(value)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// EncodeMsgpack encodes the OptionalDecimal value using MessagePack format.
+// - If the value is present, it is encoded as Decimal.
+// - If the value is absent (None), it is encoded as nil.
+//
+// Returns an error if encoding fails.
+func (o OptionalDecimal) EncodeMsgpack(encoder *msgpack.Encoder) error {
+ if o.exists {
+ return o.newEncodeError(o.encodeValue(encoder))
+ }
+
+ return o.newEncodeError(encoder.EncodeNil())
+}
+
+func (o *OptionalDecimal) decodeValue(decoder *msgpack.Decoder) error {
+ tp, length, err := decoder.DecodeExtHeader()
+ switch {
+ case err != nil:
+ return o.newDecodeError(err)
+ case tp != 1:
+ return o.newDecodeError(fmt.Errorf("invalid extension code: %d", tp))
+ }
+
+ a := make([]byte, length)
+ if err := decoder.ReadFull(a); err != nil {
+ return o.newDecodeError(err)
+ }
+
+ if err := o.value.UnmarshalMsgpack(a); err != nil {
+ return o.newDecodeError(err)
+ }
+
+ o.exists = true
+ return nil
+}
+
+func (o *OptionalDecimal) checkCode(code byte) bool {
+ return msgpcode.IsExt(code)
+}
+
+// DecodeMsgpack decodes a OptionalDecimal value from MessagePack format.
+// Supports two input types:
+// - nil: interpreted as no value (NoneOptionalDecimal)
+// - Decimal: interpreted as a present value (SomeOptionalDecimal)
+//
+// Returns an error if the input type is unsupported or decoding fails.
+//
+// After successful decoding:
+// - on nil: exists = false, value = default zero value
+// - on Decimal: exists = true, value = decoded value
+func (o *OptionalDecimal) DecodeMsgpack(decoder *msgpack.Decoder) error {
+ code, err := decoder.PeekCode()
+ if err != nil {
+ return o.newDecodeError(err)
+ }
+
+ switch {
+ case code == msgpcode.Nil:
+ o.exists = false
+
+ return o.newDecodeError(decoder.Skip())
+ case o.checkCode(code):
+ err := o.decodeValue(decoder)
+ if err != nil {
+ return o.newDecodeError(err)
+ }
+ o.exists = true
+
+ return err
+ default:
+ return o.newDecodeError(fmt.Errorf("unexpected code: %d", code))
+ }
+}
diff --git a/decimal/decimal_gen_test.go b/decimal/decimal_gen_test.go
new file mode 100644
index 000000000..50f22bf23
--- /dev/null
+++ b/decimal/decimal_gen_test.go
@@ -0,0 +1,117 @@
+package decimal
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/shopspring/decimal"
+ "github.com/stretchr/testify/assert"
+ "github.com/vmihailenco/msgpack/v5"
+)
+
+func TestSomeOptionalDecimal(t *testing.T) {
+ val := MakeDecimal(decimal.NewFromFloat(1.23))
+ opt := SomeOptionalDecimal(val)
+
+ assert.True(t, opt.IsSome())
+ assert.False(t, opt.IsZero())
+
+ v, ok := opt.Get()
+ assert.True(t, ok)
+ assert.Equal(t, val, v)
+}
+
+func TestNoneOptionalDecimal(t *testing.T) {
+ opt := NoneOptionalDecimal()
+
+ assert.False(t, opt.IsSome())
+ assert.True(t, opt.IsZero())
+
+ _, ok := opt.Get()
+ assert.False(t, ok)
+}
+
+func TestOptionalDecimal_MustGet(t *testing.T) {
+ val := MakeDecimal(decimal.NewFromFloat(1.23))
+ optSome := SomeOptionalDecimal(val)
+ optNone := NoneOptionalDecimal()
+
+ assert.Equal(t, val, optSome.MustGet())
+ assert.Panics(t, func() { optNone.MustGet() })
+}
+
+func TestOptionalDecimal_Unwrap(t *testing.T) {
+ val := MakeDecimal(decimal.NewFromFloat(1.23))
+ optSome := SomeOptionalDecimal(val)
+ optNone := NoneOptionalDecimal()
+
+ assert.Equal(t, val, optSome.Unwrap())
+ assert.Equal(t, Decimal{}, optNone.Unwrap())
+}
+
+func TestOptionalDecimal_UnwrapOr(t *testing.T) {
+ val := MakeDecimal(decimal.NewFromFloat(1.23))
+ def := MakeDecimal(decimal.NewFromFloat(4.56))
+ optSome := SomeOptionalDecimal(val)
+ optNone := NoneOptionalDecimal()
+
+ assert.Equal(t, val, optSome.UnwrapOr(def))
+ assert.Equal(t, def, optNone.UnwrapOr(def))
+}
+
+func TestOptionalDecimal_UnwrapOrElse(t *testing.T) {
+ val := MakeDecimal(decimal.NewFromFloat(1.23))
+ def := MakeDecimal(decimal.NewFromFloat(4.56))
+ optSome := SomeOptionalDecimal(val)
+ optNone := NoneOptionalDecimal()
+
+ assert.Equal(t, val, optSome.UnwrapOrElse(func() Decimal { return def }))
+ assert.Equal(t, def, optNone.UnwrapOrElse(func() Decimal { return def }))
+}
+
+func TestOptionalDecimal_EncodeDecodeMsgpack_Some(t *testing.T) {
+ val := MakeDecimal(decimal.NewFromFloat(1.23))
+ some := SomeOptionalDecimal(val)
+
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(some)
+ assert.NoError(t, err)
+
+ var decodedSome OptionalDecimal
+ err = dec.Decode(&decodedSome)
+ assert.NoError(t, err)
+ assert.True(t, decodedSome.IsSome())
+ assert.Equal(t, val, decodedSome.Unwrap())
+}
+
+func TestOptionalDecimal_EncodeDecodeMsgpack_None(t *testing.T) {
+ none := NoneOptionalDecimal()
+
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(none)
+ assert.NoError(t, err)
+
+ var decodedNone OptionalDecimal
+ err = dec.Decode(&decodedNone)
+ assert.NoError(t, err)
+ assert.True(t, decodedNone.IsZero())
+}
+
+func TestOptionalDecimal_EncodeDecodeMsgpack_InvalidType(t *testing.T) {
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(123)
+ assert.NoError(t, err)
+
+ var decodedInvalid OptionalDecimal
+ err = dec.Decode(&decodedInvalid)
+ assert.Error(t, err)
+}
diff --git a/decimal/decimal_test.go b/decimal/decimal_test.go
index 573daa8f6..f75494204 100644
--- a/decimal/decimal_test.go
+++ b/decimal/decimal_test.go
@@ -12,9 +12,9 @@ import (
"github.com/shopspring/decimal"
"github.com/vmihailenco/msgpack/v5"
- . "github.com/tarantool/go-tarantool/v2"
- . "github.com/tarantool/go-tarantool/v2/decimal"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ . "github.com/tarantool/go-tarantool/v3"
+ . "github.com/tarantool/go-tarantool/v3/decimal"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
var isDecimalSupported = false
diff --git a/decimal/example_test.go b/decimal/example_test.go
index 5597590dc..7903b077e 100644
--- a/decimal/example_test.go
+++ b/decimal/example_test.go
@@ -13,8 +13,8 @@ import (
"log"
"time"
- "github.com/tarantool/go-tarantool/v2"
- . "github.com/tarantool/go-tarantool/v2/decimal"
+ "github.com/tarantool/go-tarantool/v3"
+ . "github.com/tarantool/go-tarantool/v3/decimal"
)
// To enable support of decimal in msgpack with
diff --git a/decimal/fuzzing_test.go b/decimal/fuzzing_test.go
index 5ec2a2b8f..b6c49dcd9 100644
--- a/decimal/fuzzing_test.go
+++ b/decimal/fuzzing_test.go
@@ -7,7 +7,8 @@ import (
"testing"
"github.com/shopspring/decimal"
- . "github.com/tarantool/go-tarantool/v2/decimal"
+
+ . "github.com/tarantool/go-tarantool/v3/decimal"
)
func strToDecimal(t *testing.T, buf string, exp int) decimal.Decimal {
diff --git a/dial_test.go b/dial_test.go
index 87b9af5d8..2d0dee8ce 100644
--- a/dial_test.go
+++ b/dial_test.go
@@ -16,8 +16,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/tarantool/go-iproto"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
type mockErrorDialer struct {
diff --git a/errors.go b/errors.go
index 60f71a2b1..32483a9f6 100644
--- a/errors.go
+++ b/errors.go
@@ -25,13 +25,13 @@ func (tnterr Error) Error() string {
// ClientError is connection error produced by this client,
// i.e. connection failures or timeouts.
type ClientError struct {
- Code uint32
+ Code CodeError
Msg string
}
// Error converts a ClientError to a string.
func (clierr ClientError) Error() string {
- return fmt.Sprintf("%s (0x%x)", clierr.Msg, clierr.Code)
+ return fmt.Sprintf("%s (%#x)", clierr.Msg, uint32(clierr.Code))
}
// Temporary returns true if next attempt to perform request may succeeded.
@@ -52,13 +52,23 @@ func (clierr ClientError) Temporary() bool {
}
}
+// CodeError is an error providing code of failure.
+// Allows to differ them and returning error using errors.Is.
+type CodeError uint32
+
+// Error converts CodeError to a string.
+func (err CodeError) Error() string {
+ return fmt.Sprintf("%#x", uint32(err))
+}
+
// Tarantool client error codes.
const (
- ErrConnectionNotReady = 0x4000 + iota
- ErrConnectionClosed = 0x4000 + iota
- ErrProtocolError = 0x4000 + iota
- ErrTimeouted = 0x4000 + iota
- ErrRateLimited = 0x4000 + iota
- ErrConnectionShutdown = 0x4000 + iota
- ErrIoError = 0x4000 + iota
+ ErrConnectionNotReady CodeError = 0x4000 + iota
+ ErrConnectionClosed
+ ErrProtocolError
+ ErrTimeouted
+ ErrRateLimited
+ ErrConnectionShutdown
+ ErrIoError
+ ErrCancelledCtx
)
diff --git a/example_custom_unpacking_test.go b/example_custom_unpacking_test.go
index a2706f3f6..d8c790a25 100644
--- a/example_custom_unpacking_test.go
+++ b/example_custom_unpacking_test.go
@@ -8,7 +8,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
type Tuple2 struct {
@@ -19,7 +19,7 @@ type Tuple2 struct {
// Same effect in a "magic" way, but slower.
type Tuple3 struct {
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Cid uint
Orig string
diff --git a/example_test.go b/example_test.go
index 463f3289c..4186c06b8 100644
--- a/example_test.go
+++ b/example_test.go
@@ -9,14 +9,14 @@ import (
"github.com/tarantool/go-iproto"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
type Tuple struct {
// Instruct msgpack to pack this struct as array, so no custom packer
// is needed.
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Id uint
Msg string
Name string
@@ -167,7 +167,7 @@ func ExamplePingRequest_Context() {
fmt.Println("Ping Error", regexp.MustCompile("[0-9]+").ReplaceAllString(err.Error(), "N"))
// Output:
// Ping Resp data []
- // Ping Error context is done (request ID N)
+ // Ping Error context is done (request ID N) (NxN)
}
func ExampleSelectRequest() {
diff --git a/future_test.go b/future_test.go
index fbb30fe62..fe13bc103 100644
--- a/future_test.go
+++ b/future_test.go
@@ -11,8 +11,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/tarantool/go-iproto"
- . "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ . "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
"github.com/vmihailenco/msgpack/v5"
)
diff --git a/go.mod b/go.mod
index 5b44eb1c5..7582412da 100644
--- a/go.mod
+++ b/go.mod
@@ -1,18 +1,29 @@
-module github.com/tarantool/go-tarantool/v2
+module github.com/tarantool/go-tarantool/v3
-go 1.20
+go 1.24
require (
- github.com/google/uuid v1.3.0
+ github.com/google/uuid v1.6.0
github.com/shopspring/decimal v1.3.1
- github.com/stretchr/testify v1.9.0
+ github.com/stretchr/testify v1.11.1
github.com/tarantool/go-iproto v1.1.0
+ github.com/tarantool/go-option v1.0.0
github.com/vmihailenco/msgpack/v5 v5.4.1
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/kr/text v0.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
+ golang.org/x/mod v0.27.0 // indirect
+ golang.org/x/sync v0.16.0 // indirect
+ golang.org/x/tools v0.36.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+
+tool (
+ github.com/tarantool/go-option/cmd/gentypes
+ golang.org/x/tools/cmd/stringer
+)
diff --git a/go.sum b/go.sum
index 099647b8c..91e1c4f25 100644
--- a/go.sum
+++ b/go.sum
@@ -1,20 +1,38 @@
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tarantool/go-iproto v1.1.0 h1:HULVOIHsiehI+FnHfM7wMDntuzUddO09DKqu2WnFQ5A=
github.com/tarantool/go-iproto v1.1.0/go.mod h1:LNCtdyZxojUed8SbOiYHoc3v9NvaZTB7p96hUySMlIo=
+github.com/tarantool/go-option v1.0.0 h1:+Etw0i3TjsXvADTo5rfZNCfsXe3BfHOs+iVfIrl0Nlo=
+github.com/tarantool/go-option v1.0.0/go.mod h1:lXzzeZtL+rPUtLOCDP6ny3FemFBjruG9aHKzNN2bS08=
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
+golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
+golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
+golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
+golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/golden_test.go b/golden_test.go
index 271a98ce3..c2ee52f89 100644
--- a/golden_test.go
+++ b/golden_test.go
@@ -17,7 +17,7 @@ import (
"github.com/tarantool/go-iproto"
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// golden_test.go contains tests that will check that the msgpack
diff --git a/pool/connection_pool.go b/pool/connection_pool.go
index e62cb2b3e..572fe6cde 100644
--- a/pool/connection_pool.go
+++ b/pool/connection_pool.go
@@ -20,7 +20,7 @@ import (
"github.com/tarantool/go-iproto"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
var (
diff --git a/pool/connection_pool_test.go b/pool/connection_pool_test.go
index 8120c613d..e0fcc8c47 100644
--- a/pool/connection_pool_test.go
+++ b/pool/connection_pool_test.go
@@ -15,12 +15,12 @@ import (
"time"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
"github.com/vmihailenco/msgpack/v5"
- "github.com/stretchr/testify/require"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/pool"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/pool"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
var user = "test"
@@ -2572,7 +2572,7 @@ func TestSelect(t *testing.T) {
err = test_helpers.InsertOnInstances(ctx, makeDialers(allServers), connOpts, spaceNo, anyTpl)
require.Nil(t, err)
- //default: ANY
+ // default: ANY
data, err := connPool.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, anyKey)
require.Nilf(t, err, "failed to Select")
require.NotNilf(t, data, "response is nil after Select")
diff --git a/pool/connector.go b/pool/connector.go
index 23cc7275d..391b83b75 100644
--- a/pool/connector.go
+++ b/pool/connector.go
@@ -5,7 +5,7 @@ import (
"fmt"
"time"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// ConnectorAdapter allows to use Pooler as Connector.
diff --git a/pool/connector_test.go b/pool/connector_test.go
index 87bebbd53..9b8106c12 100644
--- a/pool/connector_test.go
+++ b/pool/connector_test.go
@@ -7,8 +7,9 @@ import (
"time"
"github.com/stretchr/testify/require"
- "github.com/tarantool/go-tarantool/v2"
- . "github.com/tarantool/go-tarantool/v2/pool"
+
+ "github.com/tarantool/go-tarantool/v3"
+ . "github.com/tarantool/go-tarantool/v3/pool"
)
var testMode Mode = RW
diff --git a/pool/const.go b/pool/const.go
index b1748ae52..d15490928 100644
--- a/pool/const.go
+++ b/pool/const.go
@@ -1,4 +1,4 @@
-//go:generate stringer -type Role -linecomment
+//go:generate go tool stringer -type Role -linecomment
package pool
/*
diff --git a/pool/example_test.go b/pool/example_test.go
index dce8bb1af..6cf339baf 100644
--- a/pool/example_test.go
+++ b/pool/example_test.go
@@ -6,15 +6,15 @@ import (
"github.com/tarantool/go-iproto"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/pool"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/pool"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
type Tuple struct {
// Instruct msgpack to pack this struct as array, so no custom packer
// is needed.
- _msgpack struct{} `msgpack:",asArray"` //nolint: structcheck,unused
+ _msgpack struct{} `msgpack:",asArray"` // nolint: structcheck,unused
Key string
Value string
}
diff --git a/pool/pooler.go b/pool/pooler.go
index d4c0f1b5e..fd3df3401 100644
--- a/pool/pooler.go
+++ b/pool/pooler.go
@@ -4,7 +4,7 @@ import (
"context"
"time"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// TopologyEditor is the interface that must be implemented by a connection pool.
diff --git a/pool/round_robin.go b/pool/round_robin.go
index 82cf26f39..f3ccb014c 100644
--- a/pool/round_robin.go
+++ b/pool/round_robin.go
@@ -4,7 +4,7 @@ import (
"sync"
"sync/atomic"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
type roundRobinStrategy struct {
diff --git a/pool/round_robin_test.go b/pool/round_robin_test.go
index 6f028f2de..dcc219fd4 100644
--- a/pool/round_robin_test.go
+++ b/pool/round_robin_test.go
@@ -3,7 +3,7 @@ package pool
import (
"testing"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
const (
diff --git a/pool/watcher.go b/pool/watcher.go
index f7c08213e..aee3103fd 100644
--- a/pool/watcher.go
+++ b/pool/watcher.go
@@ -3,7 +3,7 @@ package pool
import (
"sync"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// watcherContainer is a very simple implementation of a thread-safe container
diff --git a/protocol_test.go b/protocol_test.go
index 81ed2d3b5..c79a8afd3 100644
--- a/protocol_test.go
+++ b/protocol_test.go
@@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/tarantool/go-iproto"
- . "github.com/tarantool/go-tarantool/v2"
+ . "github.com/tarantool/go-tarantool/v3"
)
func TestProtocolInfoClonePreservesFeatures(t *testing.T) {
diff --git a/queue/example_connection_pool_test.go b/queue/example_connection_pool_test.go
index a126e13a1..8b5aab7cb 100644
--- a/queue/example_connection_pool_test.go
+++ b/queue/example_connection_pool_test.go
@@ -8,10 +8,11 @@ import (
"time"
"github.com/google/uuid"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/pool"
- "github.com/tarantool/go-tarantool/v2/queue"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/pool"
+ "github.com/tarantool/go-tarantool/v3/queue"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
// QueueConnectionHandler handles new connections in a ConnectionPool.
diff --git a/queue/example_msgpack_test.go b/queue/example_msgpack_test.go
index cdd1a4e1c..53e54dc72 100644
--- a/queue/example_msgpack_test.go
+++ b/queue/example_msgpack_test.go
@@ -16,8 +16,8 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/queue"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/queue"
)
type dummyData struct {
diff --git a/queue/example_test.go b/queue/example_test.go
index 1b411603a..99efa769b 100644
--- a/queue/example_test.go
+++ b/queue/example_test.go
@@ -13,8 +13,8 @@ import (
"fmt"
"time"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/queue"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/queue"
)
// Example demonstrates an operations like Put and Take with queue.
diff --git a/queue/queue.go b/queue/queue.go
index 99b1a722f..c8f968dff 100644
--- a/queue/queue.go
+++ b/queue/queue.go
@@ -15,7 +15,7 @@ import (
"github.com/google/uuid"
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// Queue is a handle to Tarantool queue's tube.
diff --git a/queue/queue_test.go b/queue/queue_test.go
index 840c18b4f..81f768e18 100644
--- a/queue/queue_test.go
+++ b/queue/queue_test.go
@@ -10,9 +10,9 @@ import (
"github.com/vmihailenco/msgpack/v5"
- . "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/queue"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ . "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/queue"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
const (
@@ -31,8 +31,8 @@ var dialer = NetDialer{
var opts = Opts{
Timeout: 5 * time.Second,
- //Concurrency: 32,
- //RateLimit: 4*1024,
+ // Concurrency: 32,
+ // RateLimit: 4*1024,
}
func createQueue(t *testing.T, conn *Connection, name string, cfg queue.Cfg) queue.Queue {
@@ -54,7 +54,7 @@ func dropQueue(t *testing.T, q queue.Queue) {
}
}
-/////////QUEUE/////////
+// ///////QUEUE/////////
func TestFifoQueue(t *testing.T) {
conn := test_helpers.ConnectWithValidation(t, dialer, opts)
diff --git a/request_test.go b/request_test.go
index 43e22d311..fb4290299 100644
--- a/request_test.go
+++ b/request_test.go
@@ -12,7 +12,7 @@ import (
"github.com/tarantool/go-iproto"
"github.com/vmihailenco/msgpack/v5"
- . "github.com/tarantool/go-tarantool/v2"
+ . "github.com/tarantool/go-tarantool/v3"
)
const invalidSpaceMsg = "invalid space"
diff --git a/response_test.go b/response_test.go
index 1edbf018e..e58b4d47c 100644
--- a/response_test.go
+++ b/response_test.go
@@ -7,8 +7,9 @@ import (
"github.com/stretchr/testify/require"
"github.com/tarantool/go-iproto"
- "github.com/tarantool/go-tarantool/v2"
"github.com/vmihailenco/msgpack/v5"
+
+ "github.com/tarantool/go-tarantool/v3"
)
func encodeResponseData(t *testing.T, data interface{}) io.Reader {
diff --git a/schema_test.go b/schema_test.go
index cbc863917..e30d52690 100644
--- a/schema_test.go
+++ b/schema_test.go
@@ -9,8 +9,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
func TestGetSchema_ok(t *testing.T) {
diff --git a/settings/example_test.go b/settings/example_test.go
index e51cadef0..fdad495f3 100644
--- a/settings/example_test.go
+++ b/settings/example_test.go
@@ -5,9 +5,9 @@ import (
"fmt"
"time"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/settings"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/settings"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
var exampleDialer = tarantool.NetDialer{
diff --git a/settings/request.go b/settings/request.go
index 1c106dc8d..10c6cac25 100644
--- a/settings/request.go
+++ b/settings/request.go
@@ -65,7 +65,7 @@ import (
"github.com/tarantool/go-iproto"
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// SetRequest helps to set session settings.
diff --git a/settings/request_test.go b/settings/request_test.go
index b4c537a29..bb6c9e5c5 100644
--- a/settings/request_test.go
+++ b/settings/request_test.go
@@ -9,8 +9,8 @@ import (
"github.com/tarantool/go-iproto"
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
- . "github.com/tarantool/go-tarantool/v2/settings"
+ "github.com/tarantool/go-tarantool/v3"
+ . "github.com/tarantool/go-tarantool/v3/settings"
)
type ValidSchemeResolver struct {
diff --git a/settings/tarantool_test.go b/settings/tarantool_test.go
index 56cee33ce..891959397 100644
--- a/settings/tarantool_test.go
+++ b/settings/tarantool_test.go
@@ -7,9 +7,10 @@ import (
"time"
"github.com/stretchr/testify/require"
- "github.com/tarantool/go-tarantool/v2"
- . "github.com/tarantool/go-tarantool/v2/settings"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+
+ "github.com/tarantool/go-tarantool/v3"
+ . "github.com/tarantool/go-tarantool/v3/settings"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
// There is no way to skip tests in testing.M,
diff --git a/shutdown_test.go b/shutdown_test.go
index 4df34aef5..434600824 100644
--- a/shutdown_test.go
+++ b/shutdown_test.go
@@ -13,8 +13,9 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- . "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+
+ . "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
var shtdnServer = "127.0.0.1:3014"
diff --git a/tarantool_test.go b/tarantool_test.go
index 4902e96f4..483a90d52 100644
--- a/tarantool_test.go
+++ b/tarantool_test.go
@@ -3,6 +3,7 @@ package tarantool_test
import (
"context"
"encoding/binary"
+ "errors"
"fmt"
"io"
"log"
@@ -23,8 +24,8 @@ import (
"github.com/tarantool/go-iproto"
"github.com/vmihailenco/msgpack/v5"
- . "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ . "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
var startOpts test_helpers.StartOpts = test_helpers.StartOpts{
@@ -48,7 +49,8 @@ type Member struct {
Val uint
}
-var contextDoneErrRegexp = regexp.MustCompile(`^context is done \(request ID [0-9]+\)$`)
+var contextDoneErrRegexp = regexp.MustCompile(
+ fmt.Sprintf(`^context is done \(request ID [0-9]+\) \(%s\)$`, ErrCancelledCtx.Error()))
func (m *Member) EncodeMsgpack(e *msgpack.Encoder) error {
if err := e.EncodeArrayLen(2); err != nil {
@@ -89,8 +91,8 @@ var indexNo = uint32(0)
var indexName = "primary"
var opts = Opts{
Timeout: 5 * time.Second,
- //Concurrency: 32,
- //RateLimit: 4*1024,
+ // Concurrency: 32,
+ // RateLimit: 4*1024,
}
const N = 500
@@ -888,7 +890,7 @@ func TestFutureMultipleGetTypedWithError(t *testing.T) {
}
}
-///////////////////
+// /////////////////
func TestClient(t *testing.T) {
var err error
@@ -2716,7 +2718,7 @@ func TestCallRequest(t *testing.T) {
func TestClientRequestObjectsWithNilContext(t *testing.T) {
conn := test_helpers.ConnectWithValidation(t, dialer, opts)
defer conn.Close()
- req := NewPingRequest().Context(nil) //nolint
+ req := NewPingRequest().Context(nil) // nolint
data, err := conn.Do(req).Get()
if err != nil {
t.Fatalf("Failed to Ping: %s", err)
@@ -2737,6 +2739,8 @@ func TestClientRequestObjectsWithPassedCanceledContext(t *testing.T) {
if !contextDoneErrRegexp.Match([]byte(err.Error())) {
t.Fatalf("Failed to catch an error from done context")
}
+ // checking that we could use errors.Is to get known about error.
+ require.True(t, errors.Is(err, ErrCancelledCtx))
if resp != nil {
t.Fatalf("Response is not nil after the occurred error")
}
@@ -3269,7 +3273,7 @@ func TestClientIdRequestObjectWithNilContext(t *testing.T) {
req := NewIdRequest(ProtocolInfo{
Version: ProtocolVersion(1),
Features: []iproto.Feature{iproto.IPROTO_FEATURE_STREAMS},
- }).Context(nil) //nolint
+ }).Context(nil) // nolint
data, err := conn.Do(req).Get()
require.Nilf(t, err, "No errors on Id request execution")
require.NotNilf(t, data, "Response data not empty")
@@ -3293,11 +3297,12 @@ func TestClientIdRequestObjectWithPassedCanceledContext(t *testing.T) {
req := NewIdRequest(ProtocolInfo{
Version: ProtocolVersion(1),
Features: []iproto.Feature{iproto.IPROTO_FEATURE_STREAMS},
- }).Context(ctx) //nolint
+ }).Context(ctx) // nolint
cancel()
resp, err := conn.Do(req).Get()
require.Nilf(t, resp, "Response is empty")
require.NotNilf(t, err, "Error is not empty")
+ require.True(t, errors.Is(err, ErrCancelledCtx))
require.Regexp(t, contextDoneErrRegexp, err.Error())
}
diff --git a/test_helpers/doer.go b/test_helpers/doer.go
index c33ff0e69..b61692c43 100644
--- a/test_helpers/doer.go
+++ b/test_helpers/doer.go
@@ -4,7 +4,7 @@ import (
"bytes"
"testing"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
type doerResponse struct {
diff --git a/test_helpers/example_test.go b/test_helpers/example_test.go
index 6272d737d..3b1ed5d64 100644
--- a/test_helpers/example_test.go
+++ b/test_helpers/example_test.go
@@ -5,8 +5,9 @@ import (
"testing"
"github.com/stretchr/testify/assert"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
func TestExampleMockDoer(t *testing.T) {
diff --git a/test_helpers/main.go b/test_helpers/main.go
index 4ebe4c622..35c57f810 100644
--- a/test_helpers/main.go
+++ b/test_helpers/main.go
@@ -24,7 +24,7 @@ import (
"strconv"
"time"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
type StartOpts struct {
diff --git a/test_helpers/pool_helper.go b/test_helpers/pool_helper.go
index 599e56594..7831b9f6e 100644
--- a/test_helpers/pool_helper.go
+++ b/test_helpers/pool_helper.go
@@ -8,8 +8,8 @@ import (
"sync"
"time"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/pool"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/pool"
)
type ListenOnInstanceArgs struct {
diff --git a/test_helpers/request.go b/test_helpers/request.go
index 3756a2b54..003a97ab3 100644
--- a/test_helpers/request.go
+++ b/test_helpers/request.go
@@ -7,7 +7,7 @@ import (
"github.com/tarantool/go-iproto"
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// MockRequest is an empty mock request used for testing purposes.
diff --git a/test_helpers/response.go b/test_helpers/response.go
index f8757f563..630ac7726 100644
--- a/test_helpers/response.go
+++ b/test_helpers/response.go
@@ -7,7 +7,7 @@ import (
"github.com/vmihailenco/msgpack/v5"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
// MockResponse is a mock response used for testing purposes.
diff --git a/test_helpers/tcs/prepare.go b/test_helpers/tcs/prepare.go
index c7b87d0f6..92a13afb1 100644
--- a/test_helpers/tcs/prepare.go
+++ b/test_helpers/tcs/prepare.go
@@ -8,8 +8,8 @@ import (
"text/template"
"time"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
const (
diff --git a/test_helpers/tcs/tcs.go b/test_helpers/tcs/tcs.go
index 1ba26a19e..a54ba5fda 100644
--- a/test_helpers/tcs/tcs.go
+++ b/test_helpers/tcs/tcs.go
@@ -7,8 +7,8 @@ import (
"net"
"testing"
- "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
+ "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
)
// ErrNotSupported identifies result of `Start()` why storage was not started.
diff --git a/test_helpers/utils.go b/test_helpers/utils.go
index 579f507c9..d844a822a 100644
--- a/test_helpers/utils.go
+++ b/test_helpers/utils.go
@@ -6,7 +6,8 @@ import (
"time"
"github.com/stretchr/testify/require"
- "github.com/tarantool/go-tarantool/v2"
+
+ "github.com/tarantool/go-tarantool/v3"
)
// ConnectWithValidation tries to connect to a Tarantool instance.
diff --git a/testdata/sidecar/main.go b/testdata/sidecar/main.go
index 971b8694c..a2c571b87 100644
--- a/testdata/sidecar/main.go
+++ b/testdata/sidecar/main.go
@@ -5,7 +5,7 @@ import (
"os"
"strconv"
- "github.com/tarantool/go-tarantool/v2"
+ "github.com/tarantool/go-tarantool/v3"
)
func main() {
diff --git a/uuid/example_test.go b/uuid/example_test.go
index c79dc35be..8673d390c 100644
--- a/uuid/example_test.go
+++ b/uuid/example_test.go
@@ -15,8 +15,9 @@ import (
"time"
"github.com/google/uuid"
- "github.com/tarantool/go-tarantool/v2"
- _ "github.com/tarantool/go-tarantool/v2/uuid"
+
+ "github.com/tarantool/go-tarantool/v3"
+ _ "github.com/tarantool/go-tarantool/v3/uuid"
)
var exampleOpts = tarantool.Opts{
diff --git a/uuid/uuid.go b/uuid/uuid.go
index cc2be736f..ca7b0ad05 100644
--- a/uuid/uuid.go
+++ b/uuid/uuid.go
@@ -1,4 +1,4 @@
-// Package with support of Tarantool's UUID data type.
+// Package uuid with support of Tarantool's UUID data type.
//
// UUID data type supported in Tarantool since 2.4.1.
//
@@ -27,6 +27,17 @@ import (
// UUID external type.
const uuid_extID = 2
+//go:generate go tool gentypes -ext-code 2 -marshal-func marshalUUID -unmarshal-func unmarshalUUID -imports "github.com/google/uuid" uuid.UUID
+
+func marshalUUID(id uuid.UUID) ([]byte, error) {
+ return id.MarshalBinary()
+}
+
+func unmarshalUUID(uuid *uuid.UUID, data []byte) error {
+ return uuid.UnmarshalBinary(data)
+}
+
+// encodeUUID encodes a uuid.UUID value into the msgpack format.
func encodeUUID(e *msgpack.Encoder, v reflect.Value) error {
id := v.Interface().(uuid.UUID)
@@ -43,6 +54,7 @@ func encodeUUID(e *msgpack.Encoder, v reflect.Value) error {
return nil
}
+// decodeUUID decodes a uuid.UUID value from the msgpack format.
func decodeUUID(d *msgpack.Decoder, v reflect.Value) error {
var bytesCount = 16
bytes := make([]byte, bytesCount)
diff --git a/uuid/uuid_gen.go b/uuid/uuid_gen.go
new file mode 100644
index 000000000..f1b1992ce
--- /dev/null
+++ b/uuid/uuid_gen.go
@@ -0,0 +1,243 @@
+// Code generated by github.com/tarantool/go-option; DO NOT EDIT.
+
+package uuid
+
+import (
+ "github.com/google/uuid"
+
+ "fmt"
+
+ "github.com/vmihailenco/msgpack/v5"
+ "github.com/vmihailenco/msgpack/v5/msgpcode"
+
+ "github.com/tarantool/go-option"
+)
+
+// OptionalUUID represents an optional value of type uuid.UUID.
+// It can either hold a valid uuid.UUID (IsSome == true) or be empty (IsZero == true).
+type OptionalUUID struct {
+ value uuid.UUID
+ exists bool
+}
+
+// SomeOptionalUUID creates an optional OptionalUUID with the given uuid.UUID value.
+// The returned OptionalUUID will have IsSome() == true and IsZero() == false.
+func SomeOptionalUUID(value uuid.UUID) OptionalUUID {
+ return OptionalUUID{
+ value: value,
+ exists: true,
+ }
+}
+
+// NoneOptionalUUID creates an empty optional OptionalUUID value.
+// The returned OptionalUUID will have IsSome() == false and IsZero() == true.
+//
+// Example:
+//
+// o := NoneOptionalUUID()
+// if o.IsZero() {
+// fmt.Println("value is absent")
+// }
+func NoneOptionalUUID() OptionalUUID {
+ return OptionalUUID{}
+}
+
+func (o OptionalUUID) newEncodeError(err error) error {
+ if err == nil {
+ return nil
+ }
+ return &option.EncodeError{
+ Type: "OptionalUUID",
+ Parent: err,
+ }
+}
+
+func (o OptionalUUID) newDecodeError(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ return &option.DecodeError{
+ Type: "OptionalUUID",
+ Parent: err,
+ }
+}
+
+// IsSome returns true if the OptionalUUID contains a value.
+// This indicates the value is explicitly set (not None).
+func (o OptionalUUID) IsSome() bool {
+ return o.exists
+}
+
+// IsZero returns true if the OptionalUUID does not contain a value.
+// Equivalent to !IsSome(). Useful for consistency with types where
+// zero value (e.g. 0, false, zero struct) is valid and needs to be distinguished.
+func (o OptionalUUID) IsZero() bool {
+ return !o.exists
+}
+
+// IsNil is an alias for IsZero.
+//
+// This method is provided for compatibility with the msgpack Encoder interface.
+func (o OptionalUUID) IsNil() bool {
+ return o.IsZero()
+}
+
+// Get returns the stored value and a boolean flag indicating its presence.
+// If the value is present, returns (value, true).
+// If the value is absent, returns (zero value of uuid.UUID, false).
+//
+// Recommended usage:
+//
+// if value, ok := o.Get(); ok {
+// // use value
+// }
+func (o OptionalUUID) Get() (uuid.UUID, bool) {
+ return o.value, o.exists
+}
+
+// MustGet returns the stored value if it is present.
+// Panics if the value is absent (i.e., IsZero() == true).
+//
+// Use with caution — only when you are certain the value exists.
+//
+// Panics with: "optional value is not set" if no value is set.
+func (o OptionalUUID) MustGet() uuid.UUID {
+ if !o.exists {
+ panic("optional value is not set")
+ }
+
+ return o.value
+}
+
+// Unwrap returns the stored value regardless of presence.
+// If no value is set, returns the zero value for uuid.UUID.
+//
+// Warning: Does not check presence. Use IsSome() before calling if you need
+// to distinguish between absent value and explicit zero value.
+func (o OptionalUUID) Unwrap() uuid.UUID {
+ return o.value
+}
+
+// UnwrapOr returns the stored value if present.
+// Otherwise, returns the provided default value.
+//
+// Example:
+//
+// o := NoneOptionalUUID()
+// v := o.UnwrapOr(someDefaultOptionalUUID)
+func (o OptionalUUID) UnwrapOr(defaultValue uuid.UUID) uuid.UUID {
+ if o.exists {
+ return o.value
+ }
+
+ return defaultValue
+}
+
+// UnwrapOrElse returns the stored value if present.
+// Otherwise, calls the provided function and returns its result.
+// Useful when the default value requires computation or side effects.
+//
+// Example:
+//
+// o := NoneOptionalUUID()
+// v := o.UnwrapOrElse(func() uuid.UUID { return computeDefault() })
+func (o OptionalUUID) UnwrapOrElse(defaultValue func() uuid.UUID) uuid.UUID {
+ if o.exists {
+ return o.value
+ }
+
+ return defaultValue()
+}
+
+func (o OptionalUUID) encodeValue(encoder *msgpack.Encoder) error {
+ value, err := marshalUUID(o.value)
+ if err != nil {
+ return err
+ }
+
+ err = encoder.EncodeExtHeader(2, len(value))
+ if err != nil {
+ return err
+ }
+
+ _, err = encoder.Writer().Write(value)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// EncodeMsgpack encodes the OptionalUUID value using MessagePack format.
+// - If the value is present, it is encoded as uuid.UUID.
+// - If the value is absent (None), it is encoded as nil.
+//
+// Returns an error if encoding fails.
+func (o OptionalUUID) EncodeMsgpack(encoder *msgpack.Encoder) error {
+ if o.exists {
+ return o.newEncodeError(o.encodeValue(encoder))
+ }
+
+ return o.newEncodeError(encoder.EncodeNil())
+}
+
+func (o *OptionalUUID) decodeValue(decoder *msgpack.Decoder) error {
+ tp, length, err := decoder.DecodeExtHeader()
+ switch {
+ case err != nil:
+ return o.newDecodeError(err)
+ case tp != 2:
+ return o.newDecodeError(fmt.Errorf("invalid extension code: %d", tp))
+ }
+
+ a := make([]byte, length)
+ if err := decoder.ReadFull(a); err != nil {
+ return o.newDecodeError(err)
+ }
+
+ if err := unmarshalUUID(&o.value, a); err != nil {
+ return o.newDecodeError(err)
+ }
+
+ o.exists = true
+ return nil
+}
+
+func (o *OptionalUUID) checkCode(code byte) bool {
+ return msgpcode.IsExt(code)
+}
+
+// DecodeMsgpack decodes a OptionalUUID value from MessagePack format.
+// Supports two input types:
+// - nil: interpreted as no value (NoneOptionalUUID)
+// - uuid.UUID: interpreted as a present value (SomeOptionalUUID)
+//
+// Returns an error if the input type is unsupported or decoding fails.
+//
+// After successful decoding:
+// - on nil: exists = false, value = default zero value
+// - on uuid.UUID: exists = true, value = decoded value
+func (o *OptionalUUID) DecodeMsgpack(decoder *msgpack.Decoder) error {
+ code, err := decoder.PeekCode()
+ if err != nil {
+ return o.newDecodeError(err)
+ }
+
+ switch {
+ case code == msgpcode.Nil:
+ o.exists = false
+
+ return o.newDecodeError(decoder.Skip())
+ case o.checkCode(code):
+ err := o.decodeValue(decoder)
+ if err != nil {
+ return o.newDecodeError(err)
+ }
+ o.exists = true
+
+ return err
+ default:
+ return o.newDecodeError(fmt.Errorf("unexpected code: %d", code))
+ }
+}
diff --git a/uuid/uuid_gen_test.go b/uuid/uuid_gen_test.go
new file mode 100644
index 000000000..616bb2314
--- /dev/null
+++ b/uuid/uuid_gen_test.go
@@ -0,0 +1,117 @@
+package uuid
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/stretchr/testify/assert"
+ "github.com/vmihailenco/msgpack/v5"
+)
+
+func TestSomeOptionalUUID(t *testing.T) {
+ val := uuid.New()
+ opt := SomeOptionalUUID(val)
+
+ assert.True(t, opt.IsSome())
+ assert.False(t, opt.IsZero())
+
+ v, ok := opt.Get()
+ assert.True(t, ok)
+ assert.Equal(t, val, v)
+}
+
+func TestNoneOptionalUUID(t *testing.T) {
+ opt := NoneOptionalUUID()
+
+ assert.False(t, opt.IsSome())
+ assert.True(t, opt.IsZero())
+
+ _, ok := opt.Get()
+ assert.False(t, ok)
+}
+
+func TestOptionalUUID_MustGet(t *testing.T) {
+ val := uuid.New()
+ optSome := SomeOptionalUUID(val)
+ optNone := NoneOptionalUUID()
+
+ assert.Equal(t, val, optSome.MustGet())
+ assert.Panics(t, func() { optNone.MustGet() })
+}
+
+func TestOptionalUUID_Unwrap(t *testing.T) {
+ val := uuid.New()
+ optSome := SomeOptionalUUID(val)
+ optNone := NoneOptionalUUID()
+
+ assert.Equal(t, val, optSome.Unwrap())
+ assert.Equal(t, uuid.Nil, optNone.Unwrap())
+}
+
+func TestOptionalUUID_UnwrapOr(t *testing.T) {
+ val := uuid.New()
+ def := uuid.New()
+ optSome := SomeOptionalUUID(val)
+ optNone := NoneOptionalUUID()
+
+ assert.Equal(t, val, optSome.UnwrapOr(def))
+ assert.Equal(t, def, optNone.UnwrapOr(def))
+}
+
+func TestOptionalUUID_UnwrapOrElse(t *testing.T) {
+ val := uuid.New()
+ def := uuid.New()
+ optSome := SomeOptionalUUID(val)
+ optNone := NoneOptionalUUID()
+
+ assert.Equal(t, val, optSome.UnwrapOrElse(func() uuid.UUID { return def }))
+ assert.Equal(t, def, optNone.UnwrapOrElse(func() uuid.UUID { return def }))
+}
+
+func TestOptionalUUID_EncodeDecodeMsgpack_Some(t *testing.T) {
+ val := uuid.New()
+ some := SomeOptionalUUID(val)
+
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(some)
+ assert.NoError(t, err)
+
+ var decodedSome OptionalUUID
+ err = dec.Decode(&decodedSome)
+ assert.NoError(t, err)
+ assert.True(t, decodedSome.IsSome())
+ assert.Equal(t, val, decodedSome.Unwrap())
+}
+
+func TestOptionalUUID_EncodeDecodeMsgpack_None(t *testing.T) {
+ none := NoneOptionalUUID()
+
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(none)
+ assert.NoError(t, err)
+
+ var decodedNone OptionalUUID
+ err = dec.Decode(&decodedNone)
+ assert.NoError(t, err)
+ assert.True(t, decodedNone.IsZero())
+}
+
+func TestOptionalUUID_EncodeDecodeMsgpack_InvalidType(t *testing.T) {
+ var buf bytes.Buffer
+ enc := msgpack.NewEncoder(&buf)
+ dec := msgpack.NewDecoder(&buf)
+
+ err := enc.Encode(123)
+ assert.NoError(t, err)
+
+ var decodedInvalid OptionalUUID
+ err = dec.Decode(&decodedInvalid)
+ assert.Error(t, err)
+}
diff --git a/uuid/uuid_test.go b/uuid/uuid_test.go
index 0ce317979..22ffd7eb5 100644
--- a/uuid/uuid_test.go
+++ b/uuid/uuid_test.go
@@ -10,9 +10,9 @@ import (
"github.com/google/uuid"
"github.com/vmihailenco/msgpack/v5"
- . "github.com/tarantool/go-tarantool/v2"
- "github.com/tarantool/go-tarantool/v2/test_helpers"
- _ "github.com/tarantool/go-tarantool/v2/uuid"
+ . "github.com/tarantool/go-tarantool/v3"
+ "github.com/tarantool/go-tarantool/v3/test_helpers"
+ _ "github.com/tarantool/go-tarantool/v3/uuid"
)
// There is no way to skip tests in testing.M,