Skip to content

Commit 13a413a

Browse files
Support UUID type in msgpack
This patch provides UUID support for all space operations and as function return result. UUID type was introduced in Tarantool 2.4.1. See more in commit messages: tarantool/tarantool@d68fc29 Closes #90
1 parent 61f3a41 commit 13a413a

File tree

5 files changed

+204
-6
lines changed

5 files changed

+204
-6
lines changed

config.lua

+21
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,24 @@ console.listen '0.0.0.0:33015'
6161

6262
--box.schema.user.revoke('guest', 'read,write,execute', 'universe')
6363

64+
-- Create space with UUID pk if supported
65+
local uuid = require('uuid')
66+
local msgpack = require('msgpack')
67+
68+
local uuid_msgpack_supported = pcall(msgpack.encode, uuid.new())
69+
if uuid_msgpack_supported then
70+
local suuid = box.schema.space.create('testUUID', {
71+
id = 524,
72+
if_not_exists = true,
73+
})
74+
suuid:create_index('primary', {
75+
type = 'tree',
76+
parts = {{ field = 1, type = 'uuid' }},
77+
if_not_exists = true
78+
})
79+
suuid:truncate()
80+
81+
box.schema.user.grant('test', 'read,write', 'space', 'testUUID', { if_not_exists = true })
82+
83+
suuid:insert({ uuid.fromstr("c8f0fa1f-da29-438c-a040-393f1126ad39") })
84+
end

go.mod

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ module github.com/tarantool/go-tarantool
33
go 1.11
44

55
require (
6+
github.com/google/uuid v1.3.0
67
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
7-
google.golang.org/appengine v1.6.6 // indirect
8+
google.golang.org/appengine v1.6.7 // indirect
89
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
9-
gopkg.in/vmihailenco/msgpack.v2 v2.9.1
10+
gopkg.in/vmihailenco/msgpack.v2 v2.9.2
1011
)

go.sum

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
22
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
3+
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
4+
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
35
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
46
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
57
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -12,9 +14,9 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
1214
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
1315
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
1416
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
15-
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
16-
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
17+
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
18+
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
1719
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
1820
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
19-
gopkg.in/vmihailenco/msgpack.v2 v2.9.1 h1:kb0VV7NuIojvRfzwslQeP3yArBqJHW9tOl4t38VS1jM=
20-
gopkg.in/vmihailenco/msgpack.v2 v2.9.1/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8=
21+
gopkg.in/vmihailenco/msgpack.v2 v2.9.2 h1:gjPqo9orRVlSAH/065qw3MsFCDpH7fa1KpiizXyllY4=
22+
gopkg.in/vmihailenco/msgpack.v2 v2.9.2/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8=

msgpack_ext.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package tarantool
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
7+
"github.com/google/uuid"
8+
"gopkg.in/vmihailenco/msgpack.v2"
9+
)
10+
11+
// UUID external type
12+
// Supported since Tarantool 2.4.1. See more in commit messages.
13+
// https://github.com/tarantool/tarantool/commit/d68fc29246714eee505bc9bbcd84a02de17972c5
14+
15+
const UUID_extId = 2
16+
17+
func encodeUUID(e *msgpack.Encoder, v reflect.Value) error {
18+
id := v.Interface().(uuid.UUID)
19+
20+
bytes, err := id.MarshalBinary()
21+
if err != nil {
22+
return fmt.Errorf("msgpack: can't marshal binary uuid: %w", err)
23+
}
24+
25+
_, err = e.Writer().Write(bytes)
26+
if err != nil {
27+
return fmt.Errorf("msgpack: can't write bytes to encoder writer: %w", err)
28+
}
29+
30+
return nil
31+
}
32+
33+
func decodeUUID(d *msgpack.Decoder, v reflect.Value) error {
34+
var bytesCount int = 16;
35+
bytes := make([]byte, bytesCount)
36+
37+
n, err := d.Buffered().Read(bytes)
38+
if err != nil {
39+
return fmt.Errorf("msgpack: can't read bytes on uuid decode: %w", err)
40+
}
41+
if n < bytesCount {
42+
return fmt.Errorf("msgpack: invalid uuid bytes count %d instead of %d", n, bytesCount)
43+
}
44+
45+
id, err := uuid.FromBytes(bytes)
46+
if err != nil {
47+
return fmt.Errorf("msgpack: can't create uuid from bytes: %w", err)
48+
}
49+
50+
v.Set(reflect.ValueOf(id))
51+
return nil
52+
}
53+
54+
func init() {
55+
msgpack.Register(reflect.TypeOf((*uuid.UUID)(nil)).Elem(), encodeUUID, decodeUUID)
56+
msgpack.RegisterExt(UUID_extId, (*uuid.UUID)(nil))
57+
}

tarantool_test.go

+117
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
. "github.com/tarantool/go-tarantool"
1111
"gopkg.in/vmihailenco/msgpack.v2"
12+
"github.com/google/uuid"
1213
)
1314

1415
type Member struct {
@@ -1005,3 +1006,119 @@ func TestComplexStructs(t *testing.T) {
10051006
return
10061007
}
10071008
}
1009+
1010+
1011+
var uuidSpace = "testUUID"
1012+
var uuidIndex = "primary"
1013+
1014+
type TupleUUID struct {
1015+
id uuid.UUID
1016+
}
1017+
1018+
func (t *TupleUUID) DecodeMsgpack(d *msgpack.Decoder) error {
1019+
var err error
1020+
var l int
1021+
if l, err = d.DecodeSliceLen(); err != nil {
1022+
return err
1023+
}
1024+
if l != 1 {
1025+
return fmt.Errorf("array len doesn't match: %d", l)
1026+
}
1027+
res, err := d.DecodeInterface()
1028+
if err != nil {
1029+
return err
1030+
}
1031+
t.id = res.(uuid.UUID)
1032+
return nil
1033+
}
1034+
1035+
func connectWithValidation(t *testing.T) *Connection {
1036+
conn, err := Connect(server, opts)
1037+
if err != nil {
1038+
t.Errorf("Failed to connect: %s", err.Error())
1039+
}
1040+
if conn == nil {
1041+
t.Errorf("conn is nil after Connect")
1042+
}
1043+
return conn
1044+
}
1045+
1046+
func skipIfUUIDUnsupported(t *testing.T, conn *Connection) {
1047+
resp, err := conn.Eval("return pcall(require('msgpack').encode, require('uuid').new())", []interface{}{})
1048+
if err != nil {
1049+
t.Errorf("Failed to Eval: %s", err.Error())
1050+
}
1051+
if resp == nil {
1052+
t.Errorf("Response is nil after Eval")
1053+
}
1054+
if len(resp.Data) < 1 {
1055+
t.Errorf("Response.Data is empty after Eval")
1056+
}
1057+
val := resp.Data[0].(bool)
1058+
if val != true {
1059+
t.Skip("Skipping test for Tarantool without UUID support in msgpack")
1060+
}
1061+
}
1062+
1063+
func TestUUIDselect(t *testing.T) {
1064+
conn := connectWithValidation(t)
1065+
defer conn.Close()
1066+
1067+
skipIfUUIDUnsupported(t, conn)
1068+
1069+
id, uuidErr := uuid.Parse("c8f0fa1f-da29-438c-a040-393f1126ad39")
1070+
if uuidErr != nil {
1071+
t.Errorf("Failed to prepare test uuid: %s", uuidErr)
1072+
}
1073+
1074+
resp, errSel := conn.Select(uuidSpace, uuidIndex, 0, 1, IterEq, []interface{}{ id })
1075+
if errSel != nil {
1076+
t.Errorf("UUID select failed: %s", errSel.Error())
1077+
}
1078+
if resp == nil {
1079+
t.Errorf("Response is nil after Select")
1080+
}
1081+
if len(resp.Data) != 1 {
1082+
t.Errorf("Response Data len != 1")
1083+
}
1084+
1085+
var tuples []TupleUUID
1086+
errTyp := conn.SelectTyped(uuidSpace, uuidIndex, 0, 1, IterEq, []interface{}{ id }, &tuples)
1087+
if errTyp != nil {
1088+
t.Errorf("Failed to SelectTyped: %s", errTyp.Error())
1089+
}
1090+
if len(tuples) != 1 {
1091+
t.Errorf("Result len of SelectTyped != 1")
1092+
}
1093+
if tuples[0].id != id {
1094+
t.Errorf("Bad value loaded from SelectTyped: %s", tuples[0].id)
1095+
}
1096+
}
1097+
1098+
func TestUUIDreplace(t *testing.T) {
1099+
conn := connectWithValidation(t)
1100+
defer conn.Close()
1101+
1102+
skipIfUUIDUnsupported(t, conn)
1103+
1104+
id, uuidErr := uuid.Parse("64d22e4d-ac92-4a23-899a-e59f34af5479")
1105+
if uuidErr != nil {
1106+
t.Errorf("Failed to prepare test uuid: %s", uuidErr)
1107+
}
1108+
1109+
_, errRep := conn.Replace(uuidSpace, []interface{}{ id })
1110+
if errRep != nil {
1111+
t.Errorf("UUID replace failed: %s", errRep)
1112+
}
1113+
1114+
resp, errSel := conn.Select(uuidSpace, uuidIndex, 0, 1, IterEq, []interface{}{ id })
1115+
if errSel != nil {
1116+
t.Errorf("UUID select failed: %s", errSel)
1117+
}
1118+
if resp == nil {
1119+
t.Errorf("Response is nil after Select")
1120+
}
1121+
if len(resp.Data) != 1 {
1122+
t.Errorf("Response Data len != 1")
1123+
}
1124+
}

0 commit comments

Comments
 (0)