forked from fl00r/go-tarantool-1.6
-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support `crud.schema` request [1] and response parsing. 1. tarantool/crud#380
- Loading branch information
1 parent
852ec7e
commit c0e656b
Showing
4 changed files
with
472 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,360 @@ | ||
package crud | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/vmihailenco/msgpack/v5" | ||
"github.com/vmihailenco/msgpack/v5/msgpcode" | ||
|
||
"github.com/tarantool/go-tarantool/v2" | ||
) | ||
|
||
func msgpackIsMap(code byte) bool { | ||
return code == msgpcode.Map16 || code == msgpcode.Map32 || msgpcode.IsFixedMap(code) | ||
} | ||
|
||
// SchemaRequest helps you to create request object to call `crud.schema` | ||
// for execution by a Connection. | ||
type SchemaRequest struct { | ||
baseRequest | ||
space OptString | ||
} | ||
|
||
// MakeSchemaRequest returns a new empty StatsRequest. | ||
func MakeSchemaRequest() SchemaRequest { | ||
req := SchemaRequest{} | ||
req.impl = newCall("crud.schema") | ||
return req | ||
} | ||
|
||
// Space sets the space name for the StatsRequest request. | ||
// Note: default value is nil. | ||
func (req SchemaRequest) Space(space string) SchemaRequest { | ||
req.space = MakeOptString(space) | ||
return req | ||
} | ||
|
||
// Body fills an encoder with the call request body. | ||
func (req SchemaRequest) Body(res tarantool.SchemaResolver, enc *msgpack.Encoder) error { | ||
if value, ok := req.space.Get(); ok { | ||
req.impl = req.impl.Args([]interface{}{value}) | ||
} else { | ||
req.impl = req.impl.Args([]interface{}{}) | ||
} | ||
|
||
return req.impl.Body(res, enc) | ||
} | ||
|
||
// Context sets a passed context to CRUD request. | ||
func (req SchemaRequest) Context(ctx context.Context) SchemaRequest { | ||
req.impl = req.impl.Context(ctx) | ||
|
||
return req | ||
} | ||
|
||
// Schema contains CRUD cluster schema definition. | ||
type Schema map[string]SpaceSchema | ||
|
||
// DecodeMsgpack provides custom msgpack decoder. | ||
func (schema *Schema) DecodeMsgpack(d *msgpack.Decoder) error { | ||
var l int | ||
|
||
code, err := d.PeekCode() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if msgpackIsArray(code) { | ||
// Process empty schema case. | ||
l, err = d.DecodeArrayLen() | ||
if err != nil { | ||
return err | ||
} | ||
if l != 0 { | ||
return fmt.Errorf("expected map or empty array, got non-empty array") | ||
} | ||
} else if msgpackIsMap(code) { | ||
l, err := d.DecodeMapLen() | ||
if err != nil { | ||
return err | ||
} | ||
*schema = make(map[string]SpaceSchema, l) | ||
|
||
for i := 0; i < l; i++ { | ||
key, err := d.DecodeString() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var spaceSchema SpaceSchema | ||
if err := d.Decode(&spaceSchema); err != nil { | ||
return err | ||
} | ||
|
||
(*schema)[key] = spaceSchema | ||
} | ||
} else { | ||
return fmt.Errorf("unexpected code=%d decoding map or empty array", code) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// SpaceSchema contains a single CRUD space schema definition. | ||
type SpaceSchema struct { | ||
Format []FieldFormat | ||
Indexes map[uint32]Index | ||
} | ||
|
||
// DecodeMsgpack provides custom msgpack decoder. | ||
func (spaceSchema *SpaceSchema) DecodeMsgpack(d *msgpack.Decoder) error { | ||
l, err := d.DecodeMapLen() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for i := 0; i < l; i++ { | ||
key, err := d.DecodeString() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
switch key { | ||
case "format": | ||
la, err := d.DecodeArrayLen() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
spaceSchema.Format = make([]FieldFormat, la) | ||
for j := 0; j < la; j++ { | ||
if err := d.Decode(&spaceSchema.Format[j]); err != nil { | ||
return err | ||
} | ||
} | ||
case "indexes": | ||
lm, err := d.DecodeMapLen() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
spaceSchema.Indexes = make(map[uint32]Index, lm) | ||
for j := 0; j < lm; j++ { | ||
indexId, err := d.DecodeUint32() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var indexObj Index | ||
if err := d.Decode(&indexObj); err != nil { | ||
return err | ||
} | ||
|
||
spaceSchema.Indexes[indexId] = indexObj | ||
} | ||
default: | ||
if err := d.Skip(); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Index contains a CRUD space index definition. | ||
type Index struct { | ||
Id uint32 | ||
Name string | ||
Type string | ||
Unique bool | ||
Parts []IndexPart | ||
} | ||
|
||
// DecodeMsgpack provides custom msgpack decoder. | ||
func (index *Index) DecodeMsgpack(d *msgpack.Decoder) error { | ||
l, err := d.DecodeMapLen() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for i := 0; i < l; i++ { | ||
key, err := d.DecodeString() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
switch key { | ||
case "id": | ||
if index.Id, err = d.DecodeUint32(); err != nil { | ||
return err | ||
} | ||
case "name": | ||
if index.Name, err = d.DecodeString(); err != nil { | ||
return err | ||
} | ||
case "type": | ||
if index.Type, err = d.DecodeString(); err != nil { | ||
return err | ||
} | ||
case "unique": | ||
if index.Unique, err = d.DecodeBool(); err != nil { | ||
return err | ||
} | ||
case "parts": | ||
la, err := d.DecodeArrayLen() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
index.Parts = make([]IndexPart, la) | ||
for j := 0; j < la; j++ { | ||
if err := d.Decode(&index.Parts[j]); err != nil { | ||
return err | ||
} | ||
} | ||
default: | ||
if err := d.Skip(); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// IndexField contains a CRUD space index part definition. | ||
type IndexPart struct { | ||
Fieldno uint32 | ||
Type string | ||
ExcludeNull bool | ||
IsNullable bool | ||
} | ||
|
||
// DecodeMsgpack provides custom msgpack decoder. | ||
func (indexPart *IndexPart) DecodeMsgpack(d *msgpack.Decoder) error { | ||
l, err := d.DecodeMapLen() | ||
if err != nil { | ||
return err | ||
} | ||
for i := 0; i < l; i++ { | ||
key, err := d.DecodeString() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
switch key { | ||
case "fieldno": | ||
if indexPart.Fieldno, err = d.DecodeUint32(); err != nil { | ||
return err | ||
} | ||
case "type": | ||
if indexPart.Type, err = d.DecodeString(); err != nil { | ||
return err | ||
} | ||
case "exclude_null": | ||
if indexPart.ExcludeNull, err = d.DecodeBool(); err != nil { | ||
return err | ||
} | ||
case "is_nullable": | ||
if indexPart.IsNullable, err = d.DecodeBool(); err != nil { | ||
return err | ||
} | ||
default: | ||
if err := d.Skip(); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// SchemaResult contains a schema request result for all spaces. | ||
type SchemaResult struct { | ||
Value Schema | ||
} | ||
|
||
// DecodeMsgpack provides custom msgpack decoder. | ||
func (schemaResult *SchemaResult) DecodeMsgpack(d *msgpack.Decoder) error { | ||
arrLen, err := d.DecodeArrayLen() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if arrLen == 0 { | ||
return fmt.Errorf("unexpected empty response array") | ||
} | ||
|
||
// DecodeMapLen inside Schema decode processes `nil` as zero length map, | ||
// so in `return nil, err` case we don't miss error info. | ||
// https://github.com/vmihailenco/msgpack/blob/3f7bd806fea698e7a9fe80979aa3512dea0a7368/decode_map.go#L79-L81 | ||
if err = d.Decode(&schemaResult.Value); err != nil { | ||
return err | ||
} | ||
|
||
if arrLen > 1 { | ||
var crudErr *Error = nil | ||
|
||
if err := d.Decode(&crudErr); err != nil { | ||
return err | ||
} | ||
|
||
if crudErr != nil { | ||
return crudErr | ||
} | ||
} | ||
|
||
for i := 2; i < arrLen; i++ { | ||
if err := d.Skip(); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// SchemaResult contains a schema request result for a single space. | ||
type SpaceSchemaResult struct { | ||
Value SpaceSchema | ||
} | ||
|
||
// DecodeMsgpack provides custom msgpack decoder. | ||
func (spaceSchemaResult *SpaceSchemaResult) DecodeMsgpack(d *msgpack.Decoder) error { | ||
arrLen, err := d.DecodeArrayLen() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if arrLen == 0 { | ||
return fmt.Errorf("unexpected empty response array") | ||
} | ||
|
||
// DecodeMapLen inside SpaceSchema decode processes `nil` as zero length map, | ||
// so in `return nil, err` case we don't miss error info. | ||
// https://github.com/vmihailenco/msgpack/blob/3f7bd806fea698e7a9fe80979aa3512dea0a7368/decode_map.go#L79-L81 | ||
if err = d.Decode(&spaceSchemaResult.Value); err != nil { | ||
return err | ||
} | ||
|
||
if arrLen > 1 { | ||
var crudErr *Error = nil | ||
|
||
if err := d.Decode(&crudErr); err != nil { | ||
return err | ||
} | ||
|
||
if crudErr != nil { | ||
return crudErr | ||
} | ||
} | ||
|
||
for i := 2; i < arrLen; i++ { | ||
if err := d.Skip(); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.