Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 9c57dc5

Browse files
committedApr 15, 2022
Support decimal data type in msgpack
This patch provides decimal support for all space operations and as function return result. Decimal type was introduced in Tarantool 2.2. See more about decimal type in [1] and [2]. To use decimal with github.com/shopspring/decimal in msgpack, import tarantool/decimal submodule. 1. https://www.tarantool.io/en/doc/latest/book/box/data_model/ 2. https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-decimal-type Lua snippet for encoding number to MsgPack representation: local decimal = require('decimal') local function mp_encode_dec(num) local dec = msgpack.encode(decimal.new(num)) return dec:gsub('.', function (c) return string.format('%02x', string.byte(c)) end) end print(mp_encode_dec(-12.34)) -- 0xd6010201234d Follows up tarantool/tarantool#692 Closes #96
1 parent 7897baf commit 9c57dc5

File tree

9 files changed

+648
-0
lines changed

9 files changed

+648
-0
lines changed
 

‎CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
1616
- Support UUID type in msgpack (#90)
1717
- Go modules support (#91)
1818
- queue-utube handling (#85)
19+
- Support decimal type in msgpack (#96)
1920

2021
### Fixed
2122

‎Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ test:
1515
go clean -testcache
1616
go test ./... -v -p 1
1717

18+
.PHONY: test-decimal
19+
test-decimal:
20+
@echo "Running tests in decimal package"
21+
go clean -testcache
22+
go test ./decimal/ -v -p 1
23+
1824
.PHONY: coverage
1925
coverage:
2026
go clean -testcache

‎decimal/bcd.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package decimal
2+
3+
import (
4+
"errors"
5+
_ "fmt"
6+
"strconv"
7+
)
8+
9+
var (
10+
ErrNumberIsNotADecimal = errors.New("Number is not a decimal.")
11+
ErrWrongExponentaRange = errors.New("Exponenta has a wrong range.")
12+
)
13+
14+
var mpDecimalSign = map[rune]byte{
15+
'+': 0x0a,
16+
'-': 0x0b,
17+
}
18+
19+
var mpIsDecimalNegative = map[byte]bool{
20+
0x0a: false,
21+
0x0b: true,
22+
0x0c: false,
23+
0x0d: true,
24+
0x0e: false,
25+
0x0f: false,
26+
}
27+
28+
var hex_digit = map[rune]byte{
29+
'1': 0x1,
30+
'2': 0x2,
31+
'3': 0x3,
32+
'4': 0x4,
33+
'5': 0x5,
34+
'6': 0x6,
35+
'7': 0x7,
36+
'8': 0x8,
37+
'9': 0x9,
38+
'0': 0x0,
39+
}
40+
41+
func MPEncodeNumberToBCD(buf string) []byte {
42+
scale := 0
43+
sign := '+'
44+
// TODO: The first nibble contains 0 if the decimal number has an even number of digits.
45+
nibbleIdx := 2 /* First nibble is for sign */
46+
byteBuf := make([]byte, 1)
47+
for i, ch := range buf {
48+
// TODO: ignore leading zeroes
49+
// Check for sign in a first nibble.
50+
if (i == 0) && (ch == '-' || ch == '+') {
51+
sign = ch
52+
continue
53+
}
54+
55+
// Remember a number of digits after the decimal point.
56+
if ch == '.' {
57+
scale = len(buf) - i - 1
58+
continue
59+
}
60+
61+
//digit := byte(ch)
62+
digit := hex_digit[ch]
63+
//fmt.Printf("DEBUG: ch %c\n", ch)
64+
//fmt.Printf("DEBUG: digit byte %x\n", digit)
65+
highNibble := nibbleIdx%2 != 0
66+
lowByte := len(byteBuf) - 1
67+
if highNibble {
68+
digit = digit << 4
69+
byteBuf = append(byteBuf, digit)
70+
} else {
71+
if nibbleIdx == 2 {
72+
byteBuf[0] = digit
73+
} else {
74+
byteBuf[lowByte] = byteBuf[lowByte] | digit
75+
}
76+
}
77+
//fmt.Printf("DEBUG: %x\n", byteBuf)
78+
nibbleIdx += 1
79+
}
80+
if nibbleIdx%2 != 0 {
81+
byteBuf = append(byteBuf, mpDecimalSign[sign])
82+
} else {
83+
lowByte := len(byteBuf) - 1
84+
byteBuf[lowByte] = byteBuf[lowByte] | mpDecimalSign[sign]
85+
}
86+
byteBuf = append([]byte{byte(scale)}, byteBuf...)
87+
//fmt.Printf("DEBUG: Encoded byteBuf %x\n", byteBuf)
88+
89+
return byteBuf
90+
}
91+
92+
func highNibble(b byte) byte {
93+
return b >> 4
94+
}
95+
96+
func lowNibble(b byte) byte {
97+
return b & 0x0f
98+
}
99+
100+
// TODO: BitReader https://go.dev/play/p/Wyr_K9YAro
101+
// The first byte of the BCD array contains the first digit of the number.
102+
// The first nibble contains 0 if the decimal number has an even number of digits.
103+
// The last byte of the BCD array contains the last digit of the number
104+
// and the final nibble that represents the number's sign.
105+
func MPDecodeNumberFromBCD(bcdBuf []byte) ([]string, error) {
106+
// Maximum decimal digits taken by a decimal representation.
107+
const DecimalMaxDigits = 38
108+
const mpScaleIdx = 0
109+
110+
scale := int32(bcdBuf[mpScaleIdx])
111+
// scale == -exponent, the exponent must be in range
112+
// [ -DecimalMaxDigits; DecimalMaxDigits )
113+
if scale < -DecimalMaxDigits || scale >= DecimalMaxDigits {
114+
return nil, ErrWrongExponentaRange
115+
}
116+
117+
bcdBuf = bcdBuf[mpScaleIdx+1:]
118+
//fmt.Printf("DEBUG: MPDecodeNumberFromBCD %x\n", bcdBuf)
119+
length := len(bcdBuf)
120+
var digits []string
121+
for i, bcd_byte := range bcdBuf {
122+
// Skip leading zeros.
123+
if len(digits) == 0 && int(bcd_byte) == 0 {
124+
continue
125+
}
126+
if high := highNibble(bcd_byte); high != 0 {
127+
digit := strconv.Itoa(int(high))
128+
digits = append(digits, digit)
129+
}
130+
low := lowNibble(bcd_byte)
131+
if int(i) != length-1 {
132+
digit := strconv.Itoa(int(low))
133+
digits = append(digits, digit)
134+
}
135+
/* TODO: Make sure every digit is less than 9 and bigger than 0 */
136+
}
137+
138+
digits = append(digits[:scale+1], digits[scale:]...)
139+
digits[scale] = "."
140+
last_byte := bcdBuf[length-1]
141+
sign := lowNibble(last_byte)
142+
if mpIsDecimalNegative[sign] {
143+
digits = append([]string{"-"}, digits...)
144+
}
145+
146+
return digits, nil
147+
}

‎decimal/config.lua

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
local decimal = require('decimal')
2+
local msgpack = require('msgpack')
3+
4+
-- Do not set listen for now so connector won't be
5+
-- able to send requests until everything is configured.
6+
box.cfg{
7+
work_dir = os.getenv("TEST_TNT_WORK_DIR"),
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 decimal_msgpack_supported = pcall(msgpack.encode, decimal.new(1))
14+
if not decimal_msgpack_supported then
15+
error('Decimal unsupported, use Tarantool 2.2 or newer')
16+
end
17+
18+
local s = box.schema.space.create('testDecimal', {
19+
id = 524,
20+
if_not_exists = true,
21+
})
22+
s:create_index('primary', {
23+
type = 'TREE',
24+
parts = {
25+
{
26+
field = 1,
27+
type = 'decimal',
28+
},
29+
},
30+
if_not_exists = true
31+
})
32+
s:truncate()
33+
34+
box.schema.user.grant('test', 'read,write', 'space', 'testDecimal', { if_not_exists = true })
35+
36+
-- Set listen only when every other thing is configured.
37+
box.cfg{
38+
listen = os.getenv("TEST_TNT_LISTEN"),
39+
}
40+
41+
require('console').start()

‎decimal/decimal.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Package with support of Tarantool's decimal data type.
2+
//
3+
// Decimal data type supported in Tarantool since 2.2.
4+
//
5+
// Since: 1.6
6+
//
7+
// See also:
8+
//
9+
// * Tarantool MessagePack extensions https://www.tarantool.io/en/doc/latest/dev_guide/internals/msgpack_extensions/#the-decimal-type
10+
//
11+
// * Tarantool data model https://www.tarantool.io/en/doc/latest/book/box/data_model/
12+
//
13+
// * Tarantool issue for support decimal type https://github.com/tarantool/tarantool/issues/692
14+
package decimal
15+
16+
import (
17+
"fmt"
18+
"reflect"
19+
"strings"
20+
21+
"github.com/shopspring/decimal"
22+
"gopkg.in/vmihailenco/msgpack.v2"
23+
)
24+
25+
// Decimal external type
26+
const decimal_extId = 1
27+
28+
func encodeDecimal(e *msgpack.Encoder, v reflect.Value) error {
29+
number := v.Interface().(decimal.Decimal)
30+
dec := number.String()
31+
bcdBuf := MPEncodeNumberToBCD(dec)
32+
_, err := e.Writer().Write(bcdBuf)
33+
if err != nil {
34+
return fmt.Errorf("msgpack: can't write bytes to encoder writer: %w", err)
35+
}
36+
37+
return nil
38+
}
39+
40+
func decodeDecimal(d *msgpack.Decoder, v reflect.Value) error {
41+
var bytesCount int = 4 // FIXME
42+
b := make([]byte, bytesCount)
43+
44+
_, err := d.Buffered().Read(b)
45+
if err != nil {
46+
return fmt.Errorf("msgpack: can't read bytes on decimal decode: %w", err)
47+
}
48+
49+
digits, err := MPDecodeNumberFromBCD(b)
50+
if err != nil {
51+
return err
52+
}
53+
str := strings.Join(digits, "")
54+
dec, err := decimal.NewFromString(str)
55+
if err != nil {
56+
return err
57+
}
58+
59+
v.Set(reflect.ValueOf(dec))
60+
61+
return nil
62+
}
63+
64+
func init() {
65+
msgpack.Register(reflect.TypeOf((*decimal.Decimal)(nil)).Elem(), encodeDecimal, decodeDecimal)
66+
msgpack.RegisterExt(decimal_extId, (*decimal.Decimal)(nil))
67+
}

‎decimal/decimal_test.go

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
package decimal_test
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
"reflect"
8+
"strings"
9+
"testing"
10+
"time"
11+
12+
"github.com/shopspring/decimal"
13+
. "github.com/tarantool/go-tarantool"
14+
. "github.com/tarantool/go-tarantool/decimal"
15+
"github.com/tarantool/go-tarantool/test_helpers"
16+
"gopkg.in/vmihailenco/msgpack.v2"
17+
)
18+
19+
// There is no way to skip tests in testing.M,
20+
// so we use this variable to pass info
21+
// to each testing.T that it should skip.
22+
var isDecimalSupported = false
23+
24+
var server = "127.0.0.1:3013"
25+
var opts = Opts{
26+
Timeout: 500 * time.Millisecond,
27+
User: "test",
28+
Pass: "test",
29+
}
30+
31+
var space = "testDecimal"
32+
var index = "primary"
33+
34+
type TupleDecimal struct {
35+
number decimal.Decimal
36+
}
37+
38+
/*
39+
var decNumbers = []struct {
40+
num string
41+
hex []byte
42+
}{
43+
{"-12.34", "d6010201234d"},
44+
{"123.456789000000000", "c70b010f0123456789000000000c"},
45+
{"0", "d501000c"},
46+
{"-0", "d501000d"},
47+
{"1", "d501001c"},
48+
{"-1", "d501001d"},
49+
{"0.1", "d501011c"},
50+
{"-0.1", "d501011d"},
51+
{"2.718281828459045", "c70a010f02718281828459045c"},
52+
{"-2.718281828459045", "c70a010f02718281828459045d"},
53+
{"3.141592653589793", "c70a010f03141592653589793c"},
54+
{"-3.141592653589793", "c70a010f03141592653589793d"},
55+
{"1234567891234567890.0987654321987654321", "c7150113012345678912345678900987654321987654321c"},
56+
{"-1234567891234567890.0987654321987654321", "c7150113012345678912345678900987654321987654321d"},
57+
{"0.0000000000000000000000000000000000001", "d501251c"},
58+
{"-0.0000000000000000000000000000000000001", "d501251d"},
59+
{"0.00000000000000000000000000000000000001", "d501261c"},
60+
{"-0.00000000000000000000000000000000000001", "d501261d"},
61+
{"99999999999999999999999999999999999999", "c7150100099999999999999999999999999999999999999c"},
62+
{"-99999999999999999999999999999999999999", "c7150100099999999999999999999999999999999999999d"},
63+
}
64+
*/
65+
66+
func (t *TupleDecimal) DecodeMsgpack(d *msgpack.Decoder) error {
67+
var err error
68+
var l int
69+
if l, err = d.DecodeSliceLen(); err != nil {
70+
return err
71+
}
72+
if l != 1 {
73+
return fmt.Errorf("Array length doesn't match: %d", l)
74+
}
75+
76+
res, err := d.DecodeInterface()
77+
if err != nil {
78+
return err
79+
}
80+
t.number = res.(decimal.Decimal)
81+
82+
return nil
83+
}
84+
85+
func connectWithValidation(t *testing.T) *Connection {
86+
conn, err := Connect(server, opts)
87+
if err != nil {
88+
t.Fatalf("Failed to connect: %s", err.Error())
89+
}
90+
if conn == nil {
91+
t.Fatalf("conn is nil after Connect")
92+
}
93+
return conn
94+
}
95+
96+
func tupleValueIsDecimal(t *testing.T, tuples []interface{}, number decimal.Decimal) {
97+
if len(tuples) != 1 {
98+
t.Fatalf("Response Data len (%d) != 1", len(tuples))
99+
}
100+
101+
if tpl, ok := tuples[0].([]interface{}); !ok {
102+
t.Fatalf("Unexpected return value body")
103+
} else {
104+
if len(tpl) != 1 {
105+
t.Fatalf("Unexpected return value body (tuple len)")
106+
}
107+
if val, ok := tpl[0].(decimal.Decimal); !ok || !val.Equal(number) {
108+
t.Fatalf("Unexpected return value body (tuple 0 field)")
109+
}
110+
}
111+
}
112+
113+
var decimalBCD = []struct {
114+
bcd []byte
115+
num string
116+
}{
117+
{[]byte{0x02, 0x01, 0x23, 0x4b}, "-12.34"},
118+
{[]byte{0x0, 0xa}, "0"}, // WRONG 00000a
119+
{[]byte{0x0, 0xb}, "-0"}, // WRONG 00000b
120+
{[]byte{0x0, 0x1a}, "1"}, // WRONG 00010a
121+
{[]byte{0x0, 0x1b}, "-1"}, // WRONG 00010b
122+
{[]byte{0x1, 0x1a}, "0.1"}, // WRONG 00010a
123+
{[]byte{0x1, 0x1b}, "-0.1"}, // WRONG 00010b
124+
{[]byte{0x25, 0x1a}, "0.0000000000000000000000000000000000001"}, // WRONG 25000000000000000000000000000000000000001a
125+
{[]byte{0x25, 0x1b}, "-0.0000000000000000000000000000000000001"}, // WRONG 25000000000000000000000000000000000000001b
126+
{[]byte{0x26, 0x1a}, "0.00000000000000000000000000000000000001"}, // WRONG 2600000000000000000000000000000000000000010a
127+
{[]byte{0x26, 0x1b}, "-0.00000000000000000000000000000000000001"}, // WRONG 2600000000000000000000000000000000000000010b
128+
{[]byte{0x03, 0x01, 0x24, 0x01, 0x0c}, "0.000000000000000000000000000000000010"},
129+
// {[]byte{0xc70b010f0123456789000000000c}, "123.456789000000000"},
130+
// {[]byte{0xc70a010f02718281828459045c}, "2.718281828459045"},
131+
// {[]byte{0xc70a010f02718281828459045d}, "-2.718281828459045"},
132+
// {[]byte{0xc70a010f03141592653589793c}, "3.141592653589793"},
133+
// {[]byte{0xc70a010f03141592653589793d}, "-3.141592653589793"},
134+
// {[]byte{0xc7150100099999999999999999999999999999999999999c}, "99999999999999999999999999999999999999"},
135+
// {[]byte{0xc7150100099999999999999999999999999999999999999d}, "-99999999999999999999999999999999999999"},
136+
// {[]byte{0xc7150113012345678912345678900987654321987654321c}, "1234567891234567890.0987654321987654321"},
137+
// {[]byte{0xc7150113012345678912345678900987654321987654321d}, "-1234567891234567890.0987654321987654321"},
138+
}
139+
140+
func TestMPEncodeNumberToBCD(t *testing.T) {
141+
for _, testcase := range decimalBCD {
142+
t.Run(testcase.num, func(t *testing.T) {
143+
buf := MPEncodeNumberToBCD(testcase.num)
144+
if reflect.DeepEqual(buf, testcase.bcd) != true {
145+
t.Fatalf("Failed to encode decimal %s to BCD (actual: %x, expected '%x)'", testcase.num, buf, testcase.bcd)
146+
t.Fatalf("Failed to encode decimal %s to BCD (actual: %x, expected '%x)'", testcase.num, buf, testcase.bcd)
147+
}
148+
})
149+
}
150+
}
151+
152+
func TestMPDecodeNumberFromBCD(t *testing.T) {
153+
for _, tt := range decimalBCD {
154+
t.Run(tt.num, func(t *testing.T) {
155+
dec_expected, err := decimal.NewFromString(tt.num)
156+
if err != nil {
157+
t.Fatalf("Failed to encode string to decimal")
158+
}
159+
num, err := MPDecodeNumberFromBCD(tt.bcd)
160+
if err != nil {
161+
t.Fatalf("Failed to decode decimal (%s) from BCD (%x) - '%x'", tt.num, tt.bcd, num)
162+
}
163+
dec_actual, err := decimal.NewFromString(strings.Join(num, ""))
164+
if err != nil {
165+
t.Fatalf("Failed to encode string to decimal")
166+
}
167+
if !dec_expected.Equal(dec_actual) {
168+
t.Fatalf("Failed to decode decimal (%s) from BCD (%x) - '%x'", tt.num, tt.bcd, num)
169+
}
170+
})
171+
}
172+
}
173+
174+
func BenchmarkMPEncodeNumberToBCD(b *testing.B) {
175+
for n := 0; n < b.N; n++ {
176+
MPEncodeNumberToBCD("-12.34")
177+
}
178+
}
179+
180+
func BenchmarkMPDecodeNumberFromBCD(b *testing.B) {
181+
buf := []byte{0x02, 0x01, 0x23, 0x4b}
182+
for n := 0; n < b.N; n++ {
183+
MPDecodeNumberFromBCD(buf)
184+
}
185+
}
186+
187+
func TestSelect(t *testing.T) {
188+
t.Skip("Broken")
189+
190+
if isDecimalSupported == false {
191+
t.Skip("Skipping test for Tarantool without decimal support in msgpack")
192+
}
193+
194+
conn := connectWithValidation(t)
195+
defer conn.Close()
196+
197+
number, err := decimal.NewFromString("-12.34")
198+
if err != nil {
199+
t.Fatalf("Failed to prepare test decimal: %s", err)
200+
}
201+
202+
var offset uint32 = 0
203+
var limit uint32 = 1
204+
resp, err := conn.Select(space, index, offset, limit, IterEq, []interface{}{number})
205+
if err != nil {
206+
t.Fatalf("Decimal select failed: %s", err.Error())
207+
}
208+
if resp == nil {
209+
t.Fatalf("Response is nil after Select")
210+
}
211+
fmt.Println(resp.Data)
212+
tupleValueIsDecimal(t, resp.Data, number)
213+
}
214+
215+
func TestInsert(t *testing.T) {
216+
if isDecimalSupported == false {
217+
t.Skip("Skipping test for Tarantool without decimal support in msgpack")
218+
}
219+
220+
conn := connectWithValidation(t)
221+
defer conn.Close()
222+
223+
number, err := decimal.NewFromString("-12.34")
224+
if err != nil {
225+
t.Fatalf("Failed to prepare test decimal: %s", err)
226+
}
227+
228+
resp, err := conn.Insert(space, []interface{}{number})
229+
if err != nil {
230+
t.Fatalf("Decimal replace failed: %s", err)
231+
}
232+
if resp == nil {
233+
t.Fatalf("Response is nil after Replace")
234+
}
235+
tupleValueIsDecimal(t, resp.Data, number)
236+
237+
resp, err = conn.Delete(space, index, []interface{}{number})
238+
if err != nil {
239+
t.Fatalf("Decimal delete failed: %s", err)
240+
}
241+
tupleValueIsDecimal(t, resp.Data, number)
242+
}
243+
244+
func TestReplace(t *testing.T) {
245+
if isDecimalSupported == false {
246+
t.Skip("Skipping test for Tarantool without decimal support in msgpack")
247+
}
248+
249+
conn := connectWithValidation(t)
250+
defer conn.Close()
251+
252+
number, err := decimal.NewFromString("-12.34")
253+
if err != nil {
254+
t.Fatalf("Failed to prepare test decimal: %s", err)
255+
}
256+
257+
respRep, errRep := conn.Replace(space, []interface{}{number})
258+
if errRep != nil {
259+
t.Fatalf("Decimal replace failed: %s", errRep)
260+
}
261+
if respRep == nil {
262+
t.Fatalf("Response is nil after Replace")
263+
}
264+
tupleValueIsDecimal(t, respRep.Data, number)
265+
266+
respSel, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{number})
267+
if errSel != nil {
268+
t.Fatalf("Decimal select failed: %s", errSel)
269+
}
270+
if respSel == nil {
271+
t.Fatalf("Response is nil after Select")
272+
}
273+
tupleValueIsDecimal(t, respSel.Data, number)
274+
}
275+
276+
// runTestMain is a body of TestMain function
277+
// (see https://pkg.go.dev/testing#hdr-Main).
278+
// Using defer + os.Exit is not works so TestMain body
279+
// is a separate function, see
280+
// https://stackoverflow.com/questions/27629380/how-to-exit-a-go-program-honoring-deferred-calls
281+
func runTestMain(m *testing.M) int {
282+
isLess, err := test_helpers.IsTarantoolVersionLess(2, 2, 0)
283+
if err != nil {
284+
log.Fatalf("Failed to extract Tarantool version: %s", err)
285+
}
286+
287+
if isLess {
288+
log.Println("Skipping decimal tests...")
289+
isDecimalSupported = false
290+
return m.Run()
291+
} else {
292+
isDecimalSupported = true
293+
}
294+
295+
instance, err := test_helpers.StartTarantool(test_helpers.StartOpts{
296+
InitScript: "config.lua",
297+
Listen: server,
298+
WorkDir: "work_dir",
299+
User: opts.User,
300+
Pass: opts.Pass,
301+
WaitStart: 100 * time.Millisecond,
302+
ConnectRetry: 3,
303+
RetryTimeout: 500 * time.Millisecond,
304+
})
305+
defer test_helpers.StopTarantoolWithCleanup(instance)
306+
307+
if err != nil {
308+
log.Fatalf("Failed to prepare test Tarantool: %s", err)
309+
}
310+
311+
return m.Run()
312+
}
313+
314+
func TestMain(m *testing.M) {
315+
code := runTestMain(m)
316+
os.Exit(code)
317+
}

‎decimal/example_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Run Tarantool instance before example execution:
2+
//
3+
// Terminal 1:
4+
// $ cd decimal
5+
// $ TEST_TNT_LISTEN=3013 TEST_TNT_WORK_DIR=$(mktemp -d -t 'tarantool.XXX') tarantool config.lua
6+
//
7+
// Terminal 2:
8+
// $ go test -v example_test.go
9+
package decimal_test
10+
11+
import (
12+
"log"
13+
"time"
14+
15+
"github.com/shopspring/decimal"
16+
"github.com/tarantool/go-tarantool"
17+
_ "github.com/tarantool/go-tarantool/decimal"
18+
)
19+
20+
// To enable support of decimal in msgpack with
21+
// https://github.com/shopspring/decimal,
22+
// import tarantool/decimal submodule.
23+
func Example() {
24+
server := "127.0.0.1:3013"
25+
opts := tarantool.Opts{
26+
Timeout: 500 * time.Millisecond,
27+
Reconnect: 1 * time.Second,
28+
MaxReconnects: 3,
29+
User: "test",
30+
Pass: "test",
31+
}
32+
client, err := tarantool.Connect(server, opts)
33+
if err != nil {
34+
log.Fatalf("Failed to connect: %s", err.Error())
35+
}
36+
37+
spaceNo := uint32(524)
38+
39+
number, err := decimal.NewFromString("-22.804")
40+
if err != nil {
41+
log.Fatalf("Failed to prepare test decimal: %s", err)
42+
}
43+
44+
resp, err := client.Replace(spaceNo, []interface{}{number})
45+
if err != nil {
46+
log.Fatalf("Decimal replace failed: %s", err)
47+
}
48+
if resp == nil {
49+
log.Fatalf("Response is nil after Replace")
50+
}
51+
52+
log.Println("Decimal tuple replace")
53+
log.Println("Error", err)
54+
log.Println("Code", resp.Code)
55+
log.Println("Data", resp.Data)
56+
}

‎go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ module github.com/tarantool/go-tarantool
33
go 1.11
44

55
require (
6+
github.com/google/go-cmp v0.5.7 // indirect
67
github.com/google/uuid v1.3.0
78
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
9+
github.com/pkg/errors v0.9.1 // indirect
10+
github.com/shopspring/decimal v1.3.1
811
google.golang.org/appengine v1.6.7 // indirect
912
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
1013
gopkg.in/vmihailenco/msgpack.v2 v2.9.2
14+
gotest.tools v2.2.0+incompatible
1115
)

‎go.sum

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,31 @@
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/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
4+
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
35
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
46
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
57
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
68
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
79
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
810
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
911
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
12+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
13+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
14+
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
15+
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
1016
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
1117
golang.org/x/net v0.0.0-20190603091049-60506f45cf65 h1:+rhAzEzT3f4JtomfC371qB+0Ola2caSKcY69NUBZrRQ=
1218
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
1319
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1420
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
1521
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
1622
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
23+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
1724
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
1825
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
1926
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
2027
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2128
gopkg.in/vmihailenco/msgpack.v2 v2.9.2 h1:gjPqo9orRVlSAH/065qw3MsFCDpH7fa1KpiizXyllY4=
2229
gopkg.in/vmihailenco/msgpack.v2 v2.9.2/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8=
30+
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
31+
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=

0 commit comments

Comments
 (0)
Please sign in to comment.