Skip to content

Commit fb84f99

Browse files
DifferentialOrangeligurio
authored andcommitted
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 [1]. To use UUID with google/uuid in msgpack, import tarantool/go-tarantool/uuid submodule. 1. tarantool/tarantool@d68fc29 Closes #90
1 parent 706ffcd commit fb84f99

File tree

9 files changed

+349
-6
lines changed

9 files changed

+349
-6
lines changed

README.md

+44
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,50 @@ func main() {
284284
}
285285
```
286286

287+
To enable support of UUID in msgpack with [google/uuid](https://github.com/google/uuid),
288+
import tarantool/uuid submodule.
289+
```go
290+
package main
291+
292+
import (
293+
"log"
294+
"time"
295+
296+
"github.com/tarantool/go-tarantool"
297+
_ "github.com/tarantool/go-tarantool/uuid"
298+
"github.com/google/uuid"
299+
)
300+
301+
func main() {
302+
server := "127.0.0.1:3013"
303+
opts := tarantool.Opts{
304+
Timeout: 500 * time.Millisecond,
305+
Reconnect: 1 * time.Second,
306+
MaxReconnects: 3,
307+
User: "test",
308+
Pass: "test",
309+
}
310+
client, err := tarantool.Connect(server, opts)
311+
if err != nil {
312+
log.Fatalf("Failed to connect: %s", err.Error())
313+
}
314+
315+
spaceNo := uint32(524)
316+
317+
id, uuidErr := uuid.Parse("c8f0fa1f-da29-438c-a040-393f1126ad39")
318+
if uuidErr != nil {
319+
log.Fatalf("Failed to prepare uuid: %s", uuidErr)
320+
}
321+
322+
resp, err := client.Replace(spaceNo, []interface{}{ id })
323+
324+
log.Println("UUID tuple replace")
325+
log.Println("Error", err)
326+
log.Println("Code", resp.Code)
327+
log.Println("Data", resp.Data)
328+
}
329+
```
330+
287331
## Schema
288332

289333
```go

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.11
44

55
require (
66
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
7-
google.golang.org/appengine v1.6.6 // indirect
7+
google.golang.org/appengine v1.6.7 // indirect
88
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
9-
gopkg.in/vmihailenco/msgpack.v2 v2.9.1
9+
gopkg.in/vmihailenco/msgpack.v2 v2.9.2
1010
)

go.sum

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
1212
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
1313
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
1414
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=
15+
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
16+
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
1717
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
1818
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=
19+
gopkg.in/vmihailenco/msgpack.v2 v2.9.2 h1:gjPqo9orRVlSAH/065qw3MsFCDpH7fa1KpiizXyllY4=
20+
gopkg.in/vmihailenco/msgpack.v2 v2.9.2/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8=

uuid/config.lua

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
local uuid = require('uuid')
2+
local msgpack = require('msgpack')
3+
4+
box.cfg{
5+
listen = 3013,
6+
wal_dir = 'xlog',
7+
snap_dir = 'snap',
8+
}
9+
10+
box.schema.user.create('test', { password = 'test' , if_not_exists = true })
11+
box.schema.user.grant('test', 'execute', 'universe', nil, { if_not_exists = true })
12+
13+
local uuid_msgpack_supported = pcall(msgpack.encode, uuid.new())
14+
if not uuid_msgpack_supported then
15+
error('UUID unsupported, use Tarantool 2.4.1 or newer')
16+
end
17+
18+
local s = box.schema.space.create('testUUID', {
19+
id = 524,
20+
if_not_exists = true,
21+
})
22+
s:create_index('primary', {
23+
type = 'tree',
24+
parts = {{ field = 1, type = 'uuid' }},
25+
if_not_exists = true
26+
})
27+
s:truncate()
28+
29+
box.schema.user.grant('test', 'read,write', 'space', 'testUUID', { if_not_exists = true })
30+
31+
s:insert({ uuid.fromstr("c8f0fa1f-da29-438c-a040-393f1126ad39") })

uuid/go.mod

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module github.com/tarantool/go-tarantool/uuid
2+
3+
go 1.11
4+
5+
require (
6+
github.com/google/uuid v1.3.0
7+
github.com/tarantool/go-tarantool v0.0.0-20211104105631-61f3a41907b6
8+
google.golang.org/appengine v1.6.7 // indirect
9+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
10+
gopkg.in/vmihailenco/msgpack.v2 v2.9.2
11+
)

uuid/go.sum

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
2+
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=
5+
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
6+
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
7+
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
8+
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
9+
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
10+
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
11+
github.com/tarantool/go-tarantool v0.0.0-20211104105631-61f3a41907b6 h1:WGaVN8FgSHg3xaiYnJvTk9bnXIgW8nAOUg5aVH4RLX8=
12+
github.com/tarantool/go-tarantool v0.0.0-20211104105631-61f3a41907b6/go.mod h1:m/mppmrDtgvS3tqUvaZRdRtlgzK1Gz/T6uGndkOItmQ=
13+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
14+
golang.org/x/net v0.0.0-20190603091049-60506f45cf65 h1:+rhAzEzT3f4JtomfC371qB+0Ola2caSKcY69NUBZrRQ=
15+
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
16+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
17+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
18+
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
19+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
20+
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
21+
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
22+
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
23+
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
24+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
25+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
26+
gopkg.in/vmihailenco/msgpack.v2 v2.9.1/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8=
27+
gopkg.in/vmihailenco/msgpack.v2 v2.9.2 h1:gjPqo9orRVlSAH/065qw3MsFCDpH7fa1KpiizXyllY4=
28+
gopkg.in/vmihailenco/msgpack.v2 v2.9.2/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8=

uuid/uuid.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package uuid
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: unexpected end of stream after %d uuid bytes", n)
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+
}

uuid/uuid_test.go

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package uuid_test
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
"time"
7+
8+
. "github.com/tarantool/go-tarantool"
9+
_ "github.com/tarantool/go-tarantool/uuid"
10+
"gopkg.in/vmihailenco/msgpack.v2"
11+
"github.com/google/uuid"
12+
)
13+
14+
var server = "127.0.0.1:3013"
15+
var opts = Opts{
16+
Timeout: 500 * time.Millisecond,
17+
User: "test",
18+
Pass: "test",
19+
}
20+
21+
var space = "testUUID"
22+
var index = "primary"
23+
24+
type TupleUUID struct {
25+
id uuid.UUID
26+
}
27+
28+
func (t *TupleUUID) DecodeMsgpack(d *msgpack.Decoder) error {
29+
var err error
30+
var l int
31+
if l, err = d.DecodeSliceLen(); err != nil {
32+
return err
33+
}
34+
if l != 1 {
35+
return fmt.Errorf("array len doesn't match: %d", l)
36+
}
37+
res, err := d.DecodeInterface()
38+
if err != nil {
39+
return err
40+
}
41+
t.id = res.(uuid.UUID)
42+
return nil
43+
}
44+
45+
func connectWithValidation(t *testing.T) *Connection {
46+
conn, err := Connect(server, opts)
47+
if err != nil {
48+
t.Errorf("Failed to connect: %s", err.Error())
49+
}
50+
if conn == nil {
51+
t.Errorf("conn is nil after Connect")
52+
}
53+
return conn
54+
}
55+
56+
func skipIfUUIDUnsupported(t *testing.T, conn *Connection) {
57+
resp, err := conn.Eval("return pcall(require('msgpack').encode, require('uuid').new())", []interface{}{})
58+
if err != nil {
59+
t.Errorf("Failed to Eval: %s", err.Error())
60+
}
61+
if resp == nil {
62+
t.Errorf("Response is nil after Eval")
63+
}
64+
if len(resp.Data) < 1 {
65+
t.Errorf("Response.Data is empty after Eval")
66+
}
67+
val := resp.Data[0].(bool)
68+
if val != true {
69+
t.Skip("Skipping test for Tarantool without UUID support in msgpack")
70+
}
71+
}
72+
73+
func tupleValueIsId(t *testing.T, tuples []interface{}, id uuid.UUID) {
74+
if len(tuples) != 1 {
75+
t.Errorf("Response Data len != 1")
76+
}
77+
78+
if tpl, ok := tuples[0].([]interface{}); !ok {
79+
t.Errorf("Unexpected return value body")
80+
} else {
81+
if len(tpl) != 1 {
82+
t.Errorf("Unexpected return value body (tuple len)")
83+
}
84+
if val, ok := tpl[0].(uuid.UUID); !ok || val != id {
85+
t.Errorf("Unexpected return value body (tuple 0 field)")
86+
}
87+
}
88+
}
89+
90+
func TestSelect(t *testing.T) {
91+
conn := connectWithValidation(t)
92+
defer conn.Close()
93+
94+
skipIfUUIDUnsupported(t, conn)
95+
96+
id, uuidErr := uuid.Parse("c8f0fa1f-da29-438c-a040-393f1126ad39")
97+
if uuidErr != nil {
98+
t.Errorf("Failed to prepare test uuid: %s", uuidErr)
99+
}
100+
101+
resp, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{ id })
102+
if errSel != nil {
103+
t.Errorf("UUID select failed: %s", errSel.Error())
104+
}
105+
if resp == nil {
106+
t.Errorf("Response is nil after Select")
107+
}
108+
tupleValueIsId(t, resp.Data, id)
109+
110+
var tuples []TupleUUID
111+
errTyp := conn.SelectTyped(space, index, 0, 1, IterEq, []interface{}{ id }, &tuples)
112+
if errTyp != nil {
113+
t.Errorf("Failed to SelectTyped: %s", errTyp.Error())
114+
}
115+
if len(tuples) != 1 {
116+
t.Errorf("Result len of SelectTyped != 1")
117+
}
118+
if tuples[0].id != id {
119+
t.Errorf("Bad value loaded from SelectTyped: %s", tuples[0].id)
120+
}
121+
}
122+
123+
func TestReplace(t *testing.T) {
124+
conn := connectWithValidation(t)
125+
defer conn.Close()
126+
127+
skipIfUUIDUnsupported(t, conn)
128+
129+
id, uuidErr := uuid.Parse("64d22e4d-ac92-4a23-899a-e59f34af5479")
130+
if uuidErr != nil {
131+
t.Errorf("Failed to prepare test uuid: %s", uuidErr)
132+
}
133+
134+
respRep, errRep := conn.Replace(space, []interface{}{ id })
135+
if errRep != nil {
136+
t.Errorf("UUID replace failed: %s", errRep)
137+
}
138+
if respRep == nil {
139+
t.Errorf("Response is nil after Replace")
140+
}
141+
tupleValueIsId(t, respRep.Data, id)
142+
143+
respSel, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{ id })
144+
if errSel != nil {
145+
t.Errorf("UUID select failed: %s", errSel)
146+
}
147+
if respSel == nil {
148+
t.Errorf("Response is nil after Select")
149+
}
150+
tupleValueIsId(t, respSel.Data, id)
151+
}

0 commit comments

Comments
 (0)