Skip to content

Commit ed87373

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

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)