Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crud: support schema #336

Merged
merged 2 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
- Support `operation_data` in `crud.Error` (#330)
- Support `fetch_latest_metadata` option for crud requests with metadata (#335)
- Support `noreturn` option for data change crud requests (#335)
- Support `crud.schema` request (#336)

oleg-jukovec marked this conversation as resolved.
Show resolved Hide resolved
### Changed

Expand Down Expand Up @@ -59,6 +60,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
crud.SelectRequest (#320)
- Incorrect options (`vshard_router`, `fields`, `bucket_id`, `mode`,
`prefer_replica`, `balance`) setup for crud.GetRequest (#335)
- Tests with crud 1.4.0 (#336)
oleg-jukovec marked this conversation as resolved.
Show resolved Hide resolved

## [1.12.0] - 2023-06-07

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ clean:
.PHONY: deps
deps: clean
( cd ./queue/testdata; $(TTCTL) rocks install queue 1.3.0 )
( cd ./crud/testdata; $(TTCTL) rocks install crud 1.3.0 )
( cd ./crud/testdata; $(TTCTL) rocks install crud 1.4.0 )

.PHONY: datetime-timezones
datetime-timezones:
Expand Down
29 changes: 29 additions & 0 deletions crud/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,32 @@ func ExampleSelectRequest_pagination() {
// [{id unsigned false} {bucket_id unsigned true} {name string false}]
// [[3006 32 bla] [3007 33 bla]]
}

func ExampleSchema() {
conn := exampleConnect()

req := crud.MakeSchemaRequest()
var result crud.SchemaResult

if err := conn.Do(req).GetTyped(&result); err != nil {
fmt.Printf("Failed to execute request: %s", err)
return
}

// Schema may differ between different Tarantool versions.
// https://github.com/tarantool/tarantool/issues/4091
// https://github.com/tarantool/tarantool/commit/17c9c034933d726925910ce5bf8b20e8e388f6e3
for spaceName, spaceSchema := range result.Value {
fmt.Printf("Space format for '%s' is as follows:\n", spaceName)

for _, field := range spaceSchema.Format {
fmt.Printf(" - field '%s' with type '%s'\n", field.Name, field.Type)
}
}

// Output:
// Space format for 'test' is as follows:
// - field 'id' with type 'unsigned'
// - field 'bucket_id' with type 'unsigned'
// - field 'name' with type 'string'
}
214 changes: 214 additions & 0 deletions crud/schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
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")
}
*schema = make(map[string]SpaceSchema, l)
} 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 `msgpack:"format"`
Indexes map[uint32]Index `msgpack:"indexes"`
}

// Index contains a CRUD space index definition.
type Index struct {
Id uint32 `msgpack:"id"`
Name string `msgpack:"name"`
Type string `msgpack:"type"`
Unique bool `msgpack:"unique"`
Parts []IndexPart `msgpack:"parts"`
}

// IndexField contains a CRUD space index part definition.
type IndexPart struct {
Fieldno uint32 `msgpack:"fieldno"`
Type string `msgpack:"type"`
ExcludeNull bool `msgpack:"exclude_null"`
IsNullable bool `msgpack:"is_nullable"`
}

// SchemaResult contains a schema request result for all spaces.
type SchemaResult struct {
Value Schema
}

// DecodeMsgpack provides custom msgpack decoder.
func (result *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(&result.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
}
oleg-jukovec marked this conversation as resolved.
Show resolved Hide resolved

// DecodeMsgpack provides custom msgpack decoder.
func (result *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(&result.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
}
Loading