Skip to content

Commit c615c84

Browse files
committed
sql: add minimal sql support
This patch adds the support of SQL in connector. Added support of positional and named arguments. Added all required constants in const.go for encoding SQL in msgpack and decoding response. Fixes #62
1 parent bec9f72 commit c615c84

File tree

5 files changed

+191
-3
lines changed

5 files changed

+191
-3
lines changed

connector.go

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type Connector interface {
1717
Call(functionName string, args interface{}) (resp *Response, err error)
1818
Call17(functionName string, args interface{}) (resp *Response, err error)
1919
Eval(expr string, args interface{}) (resp *Response, err error)
20+
Execute(expr string, args ...interface{}) (resp *Response, err error)
2021

2122
GetTyped(space, index interface{}, key interface{}, result interface{}) (err error)
2223
SelectTyped(space, index interface{}, offset, limit, iterator uint32, key interface{}, result interface{}) (err error)

const.go

+16
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const (
1111
EvalRequest = 8
1212
UpsertRequest = 9
1313
Call17Request = 10
14+
ExecuteRequest = 11
1415
PingRequest = 64
1516
SubscribeRequest = 66
1617

@@ -29,6 +30,19 @@ const (
2930
KeyDefTuple = 0x28
3031
KeyData = 0x30
3132
KeyError = 0x31
33+
KeyMetaData = 0x32
34+
KeySQLText = 0x40
35+
KeySQLBind = 0x41
36+
KeySQLInfo = 0x42
37+
38+
KeyFieldName = 0x00
39+
KeyFieldType = 0x01
40+
KeyFieldColl = 0x02
41+
KeyFieldIsNullable = 0x03
42+
KeyIsAutoincrement = 0x04
43+
KeyFieldSpan = 0x05
44+
KeySQLInfoRowCount = 0x00
45+
KeySqlInfoAutoincrementIds = 0x01
3246

3347
// https://github.com/fl00r/go-tarantool-1.6/issues/2
3448

@@ -49,4 +63,6 @@ const (
4963
OkCode = uint32(0)
5064
ErrorCodeBit = 0x8000
5165
PacketLengthBytes = 5
66+
ErSpaceExistsCode = 0xa
67+
IteratorCode = 0x14
5268
)

multi/multi.go

+4
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,10 @@ func (connMulti *ConnectionMulti) Eval(expr string, args interface{}) (resp *tar
294294
return connMulti.getCurrentConnection().Eval(expr, args)
295295
}
296296

297+
func (connMulti *ConnectionMulti) Execute(expr string, args ...interface{}) (resp *tarantool.Response, err error) {
298+
return connMulti.getCurrentConnection().Execute(expr, args...)
299+
}
300+
297301
func (connMulti *ConnectionMulti) GetTyped(space, index interface{}, key interface{}, result interface{}) (err error) {
298302
return connMulti.getCurrentConnection().GetTyped(space, index, key, result)
299303
}

request.go

+85
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package tarantool
22

33
import (
44
"errors"
5+
"reflect"
6+
"strings"
57
"time"
68

79
"gopkg.in/vmihailenco/msgpack.v2"
@@ -120,6 +122,16 @@ func (conn *Connection) Eval(expr string, args interface{}) (resp *Response, err
120122
return conn.EvalAsync(expr, args).Get()
121123
}
122124

125+
// Execute passes sql expression for execution.
126+
//
127+
// It is equal to conn.ExecuteAsync(space, tuple).Get().
128+
func (conn *Connection) Execute(expr string, args ...interface{}) (resp *Response, err error) {
129+
if len(args) > 1 {
130+
return conn.ExecuteAsync(expr, args).Get()
131+
}
132+
return conn.ExecuteAsync(expr, args[0]).Get()
133+
}
134+
123135
// single used for conn.GetTyped for decode one tuple
124136
type single struct {
125137
res interface{}
@@ -346,9 +358,82 @@ func (conn *Connection) EvalAsync(expr string, args interface{}) *Future {
346358
})
347359
}
348360

361+
// ExecuteAsync sends a sql expression for execution and returns Future.
362+
func (conn *Connection) ExecuteAsync(expr string, args interface{}) *Future {
363+
future := conn.newFuture(ExecuteRequest)
364+
return future.send(conn, func(enc *msgpack.Encoder) error {
365+
enc.EncodeMapLen(2)
366+
enc.EncodeUint64(KeySQLText)
367+
enc.EncodeString(expr)
368+
enc.EncodeUint64(KeySQLBind)
369+
return encodeSQLBind(enc, args)
370+
})
371+
}
372+
349373
//
350374
// private
351375
//
376+
func encodeSQLBind(enc *msgpack.Encoder, from interface{}) error {
377+
val := reflect.ValueOf(from)
378+
switch val.Kind() {
379+
case reflect.Map:
380+
if err := enc.EncodeSliceLen(val.Len()); err != nil {
381+
return err
382+
}
383+
it := val.MapRange()
384+
for it.Next() {
385+
if err := enc.EncodeMapLen(1); err != nil {
386+
return err
387+
}
388+
if err := enc.EncodeValue(reflect.ValueOf(":" + it.Key().String())); err != nil {
389+
return err
390+
}
391+
if err := enc.EncodeValue(reflect.ValueOf(it.Value().Interface())); err != nil {
392+
return err
393+
}
394+
}
395+
case reflect.Struct:
396+
if err := enc.EncodeSliceLen(val.NumField()); err != nil {
397+
return err
398+
}
399+
for i := 0; i < val.NumField(); i++ {
400+
if err := enc.EncodeMapLen(1); err != nil {
401+
return err
402+
}
403+
key := val.Type().Field(i).Name
404+
if err := enc.EncodeValue(reflect.ValueOf(":" + strings.ToLower(key))); err != nil {
405+
return err
406+
}
407+
if err := enc.EncodeValue(reflect.ValueOf(from).FieldByName(key)); err != nil {
408+
return err
409+
}
410+
}
411+
case reflect.Slice, reflect.Array:
412+
if val.Len() > 0 {
413+
slType := reflect.ValueOf(val.Index(0).Interface()).Kind()
414+
if slType != reflect.Struct {
415+
return enc.Encode(from)
416+
}
417+
}
418+
if err := enc.EncodeSliceLen(val.Len()); err != nil {
419+
return err
420+
}
421+
for i := 0; i < val.Len(); i++ {
422+
k := val.Index(i).Field(0).String()
423+
v := val.Index(i).Field(1).Interface()
424+
if err := enc.EncodeMapLen(1); err != nil {
425+
return err
426+
}
427+
if err := enc.EncodeValue(reflect.ValueOf(":" + k)); err != nil {
428+
return err
429+
}
430+
if err := enc.EncodeValue(reflect.ValueOf(v)); err != nil {
431+
return err
432+
}
433+
}
434+
}
435+
return nil
436+
}
352437

353438
func (fut *Future) pack(h *smallWBuf, enc *msgpack.Encoder, body func(*msgpack.Encoder) error) (err error) {
354439
rid := fut.requestId

response.go

+85-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package tarantool
22

33
import (
44
"fmt"
5-
65
"gopkg.in/vmihailenco/msgpack.v2"
76
)
87

@@ -11,8 +10,75 @@ type Response struct {
1110
Code uint32
1211
Error string // error message
1312
// Data contains deserialized data for untyped requests
14-
Data []interface{}
15-
buf smallBuf
13+
Data []interface{}
14+
MetaData SQLMetaData
15+
SQLInfo SQLInfo
16+
buf smallBuf
17+
}
18+
19+
type SQLMetaData struct {
20+
ColumnsInfo []ColumnMetaData
21+
}
22+
23+
type ColumnMetaData struct {
24+
FieldName string
25+
FieldType string
26+
FieldCollation string
27+
FieldIsNullable bool
28+
FieldIsAutoincrement bool
29+
FieldSpan string
30+
}
31+
32+
type SQLInfo struct {
33+
AffectedCount uint64
34+
InfoAutoincrementIds []uint64
35+
}
36+
37+
func parseMetaData(from interface{}) SQLMetaData {
38+
metaData := SQLMetaData{
39+
ColumnsInfo: []ColumnMetaData{},
40+
}
41+
42+
arr := from.([]interface{})
43+
44+
for i := 0; i < len(arr); i++ {
45+
mp := arr[i].(map[interface{}]interface{})
46+
cd := ColumnMetaData{}
47+
cd.FieldName = mp[uint64(KeyFieldName)].(string)
48+
cd.FieldType = mp[uint64(KeyFieldType)].(string)
49+
if mp[uint64(KeyFieldColl)] != nil {
50+
cd.FieldCollation = mp[uint64(KeyFieldColl)].(string)
51+
}
52+
if mp[uint64(KeyFieldIsNullable)] != nil {
53+
cd.FieldIsNullable = mp[uint64(KeyFieldIsNullable)].(bool)
54+
}
55+
if mp[uint64(KeyIsAutoincrement)] != nil {
56+
cd.FieldIsAutoincrement = mp[uint64(KeyIsAutoincrement)].(bool)
57+
}
58+
if mp[uint64(KeyFieldSpan)] != nil {
59+
cd.FieldSpan = mp[uint64(KeyFieldSpan)].(string)
60+
}
61+
metaData.ColumnsInfo = append(metaData.ColumnsInfo, cd)
62+
}
63+
64+
return metaData
65+
}
66+
67+
func parseSQLInfo(from interface{}) SQLInfo {
68+
info := SQLInfo{}
69+
mp, ok := from.(map[interface{}]interface{})
70+
if ok {
71+
info.AffectedCount = mp[uint64(KeySQLInfoRowCount)].(uint64)
72+
if mp[uint64(KeySqlInfoAutoincrementIds)] != nil {
73+
t := mp[uint64(KeySqlInfoAutoincrementIds)].([]interface{})
74+
ids := make([]uint64, len(t))
75+
for i := range t {
76+
ids[i] = t[i].(uint64)
77+
}
78+
info.InfoAutoincrementIds = ids
79+
}
80+
}
81+
return info
1682
}
1783

1884
func (resp *Response) fill(b []byte) {
@@ -90,6 +156,22 @@ func (resp *Response) decodeBody() (err error) {
90156
if resp.Error, err = d.DecodeString(); err != nil {
91157
return err
92158
}
159+
case KeySQLInfo:
160+
var res interface{}
161+
if res, err = d.DecodeInterface(); err != nil {
162+
return err
163+
}
164+
resp.SQLInfo = parseSQLInfo(res)
165+
case KeyMetaData:
166+
var res interface{}
167+
var ok bool
168+
if res, err = d.DecodeInterface(); err != nil {
169+
return err
170+
}
171+
if _, ok = res.([]interface{}); !ok {
172+
return fmt.Errorf("result is not array: %v", res)
173+
}
174+
resp.MetaData = parseMetaData(res.([]interface{}))
93175
default:
94176
if err = d.Skip(); err != nil {
95177
return err

0 commit comments

Comments
 (0)