diff options
author | wmin0 <wmin0@cobinhood.com> | 2019-02-12 11:09:34 +0800 |
---|---|---|
committer | Jhih-Ming Huang <jm.huang@cobinhood.com> | 2019-03-26 17:48:21 +0800 |
commit | 3292a673720e99d56495bc07ff0fc03f325c0737 (patch) | |
tree | 2f7c353203d216489ab6b6b0e665e6314427bf6f /core/vm/sqlvm/schema | |
parent | 457f0a5a26a4dc97c02d9d442ab7425d53a918a8 (diff) | |
download | dexon-3292a673720e99d56495bc07ff0fc03f325c0737.tar dexon-3292a673720e99d56495bc07ff0fc03f325c0737.tar.gz dexon-3292a673720e99d56495bc07ff0fc03f325c0737.tar.bz2 dexon-3292a673720e99d56495bc07ff0fc03f325c0737.tar.lz dexon-3292a673720e99d56495bc07ff0fc03f325c0737.tar.xz dexon-3292a673720e99d56495bc07ff0fc03f325c0737.tar.zst dexon-3292a673720e99d56495bc07ff0fc03f325c0737.zip |
core: vm: sqlvm: add schema define and implement rlp serialization
Implement schema struct and handle its rlp serialization.
Diffstat (limited to 'core/vm/sqlvm/schema')
-rw-r--r-- | core/vm/sqlvm/schema/schema.go | 139 | ||||
-rw-r--r-- | core/vm/sqlvm/schema/schema_test.go | 127 |
2 files changed, 266 insertions, 0 deletions
diff --git a/core/vm/sqlvm/schema/schema.go b/core/vm/sqlvm/schema/schema.go new file mode 100644 index 000000000..4529fd7d5 --- /dev/null +++ b/core/vm/sqlvm/schema/schema.go @@ -0,0 +1,139 @@ +package schema + +import ( + "errors" + "io" + + "github.com/shopspring/decimal" + + "github.com/dexon-foundation/dexon/core/vm/sqlvm/ast" + "github.com/dexon-foundation/dexon/rlp" +) + +// Error defines for encode and decode. +var ( + ErrEncodeUnexpectedType = errors.New("encode unexpected type") + ErrDecodeUnexpectedType = errors.New("decode unexpected type") +) + +// ColumnAttr defines bit flags for describing column attribute. +type ColumnAttr uint16 + +// ColumnAttr enums. +const ( + ColumnAttrHasDefault ColumnAttr = 1 << iota + ColumnAttrNotNull + ColumnAttrHasSequence + ColumnAttrHasForeignKey +) + +// IndexAttr defines bit flags for describing index attribute. +type IndexAttr uint16 + +// IndexAttr enums. +const ( + IndexAttrUnique IndexAttr = 1 << iota +) + +// Schema defines sqlvm schema struct. +type Schema []*Table + +// Table defiens sqlvm table struct. +type Table struct { + Name []byte + Columns []*Column + Indices []*Index +} + +// Index defines sqlvm index struct. +type Index struct { + Name []byte + Attr IndexAttr + Columns []uint8 +} + +type column struct { + Name []byte + Type ast.DataType + Attr ColumnAttr + Sequence uint8 + ForeignTable uint8 + ForeignColumn uint8 + Rest interface{} +} + +// Column defines sqlvm index struct. +type Column struct { + column + Default interface{} // decimal.Decimal, bool, []byte +} + +var _ rlp.Decoder = (*Column)(nil) +var _ rlp.Encoder = Column{} + +// EncodeRLP encodes column with rlp encode. +func (c Column) EncodeRLP(w io.Writer) error { + if c.Default != nil { + switch d := c.Default.(type) { + case bool: + v := byte(0) + if d { + v = byte(1) + } + c.Rest = []byte{v} + case []byte: + c.Rest = d + case decimal.Decimal: + var err error + c.Rest, err = ast.DecimalEncode(c.Type, d) + if err != nil { + return err + } + default: + return ErrEncodeUnexpectedType + } + } else { + c.Rest = nil + } + + return rlp.Encode(w, c.column) +} + +// DecodeRLP decodes column with rlp decode. +func (c *Column) DecodeRLP(s *rlp.Stream) error { + defer func() { c.Rest = nil }() + + err := s.Decode(&c.column) + if err != nil { + return err + } + + switch rest := c.Rest.(type) { + case []interface{}: + // nil is converted to empty list by encoder, while empty list is + // converted to []interface{} by decoder. + // So we view this case as nil and skip it. + case []byte: + major, _ := ast.DecomposeDataType(c.Type) + switch major { + case ast.DataTypeMajorBool: + if rest[0] == 1 { + c.Default = true + } else { + c.Default = false + } + case ast.DataTypeMajorFixedBytes, ast.DataTypeMajorDynamicBytes: + c.Default = rest + default: + d, err := ast.DecimalDecode(c.Type, rest) + if err != nil { + return err + } + c.Default = d + } + default: + return ErrDecodeUnexpectedType + } + + return nil +} diff --git a/core/vm/sqlvm/schema/schema_test.go b/core/vm/sqlvm/schema/schema_test.go new file mode 100644 index 000000000..92bfa6c91 --- /dev/null +++ b/core/vm/sqlvm/schema/schema_test.go @@ -0,0 +1,127 @@ +package schema + +import ( + "bufio" + "bytes" + "io/ioutil" + "testing" + + "github.com/dexon-foundation/dexon/core/vm/sqlvm/ast" + "github.com/dexon-foundation/dexon/rlp" + "github.com/shopspring/decimal" + "github.com/stretchr/testify/suite" +) + +type SchemaTestSuite struct{ suite.Suite } + +func (s *SchemaTestSuite) requireEncodeAndDecodeColumnNoError(c Column) { + buffer := bytes.Buffer{} + w := bufio.NewWriter(&buffer) + s.Require().NoError(rlp.Encode(w, c)) + w.Flush() + + c2 := Column{} + r := ioutil.NopCloser(bufio.NewReader(&buffer)) + s.Require().NoError(rlp.Decode(r, &c2)) + s.Require().Equal(c, c2) +} + +func (s *SchemaTestSuite) TestEncodeAndDecodeColumn() { + s.requireEncodeAndDecodeColumnNoError(Column{ + column: column{ + Name: []byte("a"), + Type: ast.ComposeDataType(ast.DataTypeMajorBool, 0), + Attr: ColumnAttrHasSequence | ColumnAttrHasDefault, + Sequence: 1, + }, + Default: true, + }) + + s.requireEncodeAndDecodeColumnNoError(Column{ + column: column{ + Name: []byte("b"), + Type: ast.ComposeDataType(ast.DataTypeMajorFixedBytes, 0), + }, + }) + + s.requireEncodeAndDecodeColumnNoError(Column{ + column: column{ + Name: []byte("c"), + Type: ast.ComposeDataType(ast.DataTypeMajorDynamicBytes, 0), + Attr: ColumnAttrNotNull | ColumnAttrHasDefault, + }, + Default: []byte{}, + }) + + s.requireEncodeAndDecodeColumnNoError(Column{ + column: column{ + Name: []byte("d"), + Type: ast.ComposeDataType(ast.DataTypeMajorUint, 0), + Attr: ColumnAttrNotNull | ColumnAttrHasDefault, + }, + Default: decimal.New(1, 0), + }) +} + +func (s *SchemaTestSuite) TestEncodeAndDecodeSchema() { + schema := Schema{ + &Table{ + Name: []byte("test"), + Columns: []*Column{ + { + column: column{ + Name: []byte("a"), + Type: ast.ComposeDataType(ast.DataTypeMajorBool, 0), + Attr: ColumnAttrHasSequence | ColumnAttrHasDefault, + Sequence: 1, + }, + Default: true, + }, + }, + Indices: []*Index{ + { + Name: []byte("idx"), + Attr: IndexAttrUnique, + Columns: []uint8{0}, + }, + }, + }, + &Table{ + Name: []byte("test2"), + }, + } + buffer := bytes.Buffer{} + w := bufio.NewWriter(&buffer) + s.Require().NoError(rlp.Encode(w, schema)) + w.Flush() + + schema2 := Schema{} + r := ioutil.NopCloser(bufio.NewReader(&buffer)) + s.Require().NoError(rlp.Decode(r, &schema2)) + + s.Require().Equal(len(schema), len(schema2)) + + for i := 0; i < len(schema); i++ { + table := schema[i] + table2 := schema2[i] + s.Require().Equal(table.Name, table2.Name) + s.Require().Equal(len(table.Columns), len(table2.Columns)) + s.Require().Equal(len(table.Indices), len(table2.Indices)) + + for j := 0; j < len(table.Columns); j++ { + column := table.Columns[j] + column2 := table.Columns[j] + s.Require().Equal(*column, *column2) + } + + for j := 0; j < len(table.Indices); j++ { + index := table.Indices[j] + index2 := table2.Indices[j] + s.Require().Equal(*index, *index2) + } + } +} + +func TestSchema(t *testing.T) { + suite.Run(t, new(SchemaTestSuite)) +} |