aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwmin0 <wmin0@cobinhood.com>2019-02-12 11:09:34 +0800
committerJhih-Ming Huang <jm.huang@cobinhood.com>2019-05-06 10:44:03 +0800
commitfa7f1118a60685b3bd8b6fed56df70813a367549 (patch)
tree9858f0d719ced53a3f5899ca32e6e42f99d3756b
parente64684dad8b773318edfabe5d7a2ca7e77f4330a (diff)
downloaddexon-fa7f1118a60685b3bd8b6fed56df70813a367549.tar.gz
dexon-fa7f1118a60685b3bd8b6fed56df70813a367549.tar.zst
dexon-fa7f1118a60685b3bd8b6fed56df70813a367549.zip
core: vm: sqlvm: add schema define and implement rlp serialization
Implement schema struct and handle its rlp serialization.
-rw-r--r--core/vm/sqlvm/ast/types.go28
-rw-r--r--core/vm/sqlvm/ast/types_test.go58
-rw-r--r--core/vm/sqlvm/schema/schema.go139
-rw-r--r--core/vm/sqlvm/schema/schema_test.go127
4 files changed, 310 insertions, 42 deletions
diff --git a/core/vm/sqlvm/ast/types.go b/core/vm/sqlvm/ast/types.go
index 80cda796c..48e7b1fb5 100644
--- a/core/vm/sqlvm/ast/types.go
+++ b/core/vm/sqlvm/ast/types.go
@@ -49,11 +49,13 @@ const (
// DataTypeUnknown for unknown data type.
const DataTypeUnknown DataType = 0
-func decomposeDataType(t DataType) (DataTypeMajor, DataTypeMinor) {
+// DecomposeDataType to major and minor part with given data type.
+func DecomposeDataType(t DataType) (DataTypeMajor, DataTypeMinor) {
return DataTypeMajor(t >> 8), DataTypeMinor(t & 0xff)
}
-func composeDataType(major DataTypeMajor, minor DataTypeMinor) DataType {
+// ComposeDataType to concrete type with major and minor part.
+func ComposeDataType(major DataTypeMajor, minor DataTypeMinor) DataType {
return (DataType(major) << 8) | DataType(minor)
}
@@ -78,10 +80,10 @@ func DataTypeEncode(n interface{}) (DataType, error) {
switch t := n.(type) {
case BoolTypeNode:
- return composeDataType(DataTypeMajorBool, 0), nil
+ return ComposeDataType(DataTypeMajorBool, 0), nil
case AddressTypeNode:
- return composeDataType(DataTypeMajorAddress, 0), nil
+ return ComposeDataType(DataTypeMajorAddress, 0), nil
case IntTypeNode:
if t.Size%8 != 0 || t.Size > 256 {
@@ -90,9 +92,9 @@ func DataTypeEncode(n interface{}) (DataType, error) {
minor := DataTypeMinor((t.Size / 8) - 1)
if t.Unsigned {
- return composeDataType(DataTypeMajorUint, minor), nil
+ return ComposeDataType(DataTypeMajorUint, minor), nil
}
- return composeDataType(DataTypeMajorInt, minor), nil
+ return ComposeDataType(DataTypeMajorInt, minor), nil
case FixedBytesTypeNode:
if t.Size%8 != 0 || t.Size > 256 {
@@ -100,10 +102,10 @@ func DataTypeEncode(n interface{}) (DataType, error) {
}
minor := DataTypeMinor((t.Size / 8) - 1)
- return composeDataType(DataTypeMajorFixedBytes, minor), nil
+ return ComposeDataType(DataTypeMajorFixedBytes, minor), nil
case DynamicBytesTypeNode:
- return composeDataType(DataTypeMajorDynamicBytes, 0), nil
+ return ComposeDataType(DataTypeMajorDynamicBytes, 0), nil
case FixedTypeNode:
if t.Size%8 != 0 || t.Size > 256 {
@@ -117,9 +119,9 @@ func DataTypeEncode(n interface{}) (DataType, error) {
major := DataTypeMajor((t.Size / 8) - 1)
minor := DataTypeMinor(t.FractionalDigits)
if t.Unsigned {
- return composeDataType(DataTypeMajorUfixed+major, minor), nil
+ return ComposeDataType(DataTypeMajorUfixed+major, minor), nil
}
- return composeDataType(DataTypeMajorFixed+major, minor), nil
+ return ComposeDataType(DataTypeMajorFixed+major, minor), nil
}
return DataTypeUnknown, ErrDataTypeEncode
@@ -127,7 +129,7 @@ func DataTypeEncode(n interface{}) (DataType, error) {
// DataTypeDecode decodes DataType into data type node.
func DataTypeDecode(t DataType) (interface{}, error) {
- major, minor := decomposeDataType(t)
+ major, minor := DecomposeDataType(t)
switch major {
// TODO(wmin0): define unsupported error for special type.
case DataTypeMajorBool:
@@ -229,7 +231,7 @@ func decimalDecode(signed bool, bs []byte) decimal.Decimal {
// DecimalEncode encodes decimal to bytes depend on data type.
func DecimalEncode(dt DataType, d decimal.Decimal) ([]byte, error) {
- major, minor := decomposeDataType(dt)
+ major, minor := DecomposeDataType(dt)
switch major {
case DataTypeMajorInt,
DataTypeMajorUint:
@@ -253,7 +255,7 @@ func DecimalEncode(dt DataType, d decimal.Decimal) ([]byte, error) {
// DecimalDecode decodes decimal from bytes.
func DecimalDecode(dt DataType, b []byte) (decimal.Decimal, error) {
- major, minor := decomposeDataType(dt)
+ major, minor := DecomposeDataType(dt)
switch major {
case DataTypeMajorInt:
return decimalDecode(true, b), nil
diff --git a/core/vm/sqlvm/ast/types_test.go b/core/vm/sqlvm/ast/types_test.go
index 31ed224fb..aa726c7ba 100644
--- a/core/vm/sqlvm/ast/types_test.go
+++ b/core/vm/sqlvm/ast/types_test.go
@@ -41,28 +41,28 @@ func (s *TypesTestSuite) requireDecodeError(input DataType) {
func (s *TypesTestSuite) TestEncodeAndDecode() {
s.requireEncodeAndDecodeNoError(
- composeDataType(DataTypeMajorBool, 0),
+ ComposeDataType(DataTypeMajorBool, 0),
BoolTypeNode{})
s.requireEncodeAndDecodeNoError(
- composeDataType(DataTypeMajorAddress, 0),
+ ComposeDataType(DataTypeMajorAddress, 0),
AddressTypeNode{})
s.requireEncodeAndDecodeNoError(
- composeDataType(DataTypeMajorInt, 1),
+ ComposeDataType(DataTypeMajorInt, 1),
IntTypeNode{Size: 16})
s.requireEncodeAndDecodeNoError(
- composeDataType(DataTypeMajorUint, 2),
+ ComposeDataType(DataTypeMajorUint, 2),
IntTypeNode{Unsigned: true, Size: 24})
s.requireEncodeAndDecodeNoError(
- composeDataType(DataTypeMajorFixedBytes, 3),
+ ComposeDataType(DataTypeMajorFixedBytes, 3),
FixedBytesTypeNode{Size: 32})
s.requireEncodeAndDecodeNoError(
- composeDataType(DataTypeMajorDynamicBytes, 0),
+ ComposeDataType(DataTypeMajorDynamicBytes, 0),
DynamicBytesTypeNode{})
s.requireEncodeAndDecodeNoError(
- composeDataType(DataTypeMajorFixed, 1),
+ ComposeDataType(DataTypeMajorFixed, 1),
FixedTypeNode{Size: 8, FractionalDigits: 1})
s.requireEncodeAndDecodeNoError(
- composeDataType(DataTypeMajorUfixed+1, 2),
+ ComposeDataType(DataTypeMajorUfixed+1, 2),
FixedTypeNode{Unsigned: true, Size: 16, FractionalDigits: 2})
}
@@ -79,15 +79,15 @@ func (s *TypesTestSuite) TestEncodeError() {
func (s *TypesTestSuite) TestDecodeError() {
s.requireDecodeError(DataTypeUnknown)
- s.requireDecodeError(composeDataType(DataTypeMajorBool, 1))
- s.requireDecodeError(composeDataType(DataTypeMajorAddress, 1))
- s.requireDecodeError(composeDataType(DataTypeMajorInt, 0x20))
- s.requireDecodeError(composeDataType(DataTypeMajorUint, 0x20))
- s.requireDecodeError(composeDataType(DataTypeMajorFixedBytes, 0x20))
- s.requireDecodeError(composeDataType(DataTypeMajorDynamicBytes, 1))
- s.requireDecodeError(composeDataType(DataTypeMajorFixed, 81))
- s.requireDecodeError(composeDataType(DataTypeMajorUfixed, 81))
- s.requireDecodeError(composeDataType(DataTypeMajorUfixed+0x20, 80))
+ s.requireDecodeError(ComposeDataType(DataTypeMajorBool, 1))
+ s.requireDecodeError(ComposeDataType(DataTypeMajorAddress, 1))
+ s.requireDecodeError(ComposeDataType(DataTypeMajorInt, 0x20))
+ s.requireDecodeError(ComposeDataType(DataTypeMajorUint, 0x20))
+ s.requireDecodeError(ComposeDataType(DataTypeMajorFixedBytes, 0x20))
+ s.requireDecodeError(ComposeDataType(DataTypeMajorDynamicBytes, 1))
+ s.requireDecodeError(ComposeDataType(DataTypeMajorFixed, 81))
+ s.requireDecodeError(ComposeDataType(DataTypeMajorUfixed, 81))
+ s.requireDecodeError(ComposeDataType(DataTypeMajorUfixed+0x20, 80))
}
func (s *TypesTestSuite) TestEncodeAndDecodeDecimal() {
@@ -96,33 +96,33 @@ func (s *TypesTestSuite) TestEncodeAndDecodeDecimal() {
neg := decimal.New(-15, 0)
s.requireEncodeAndDecodeDecimalNoError(
- composeDataType(DataTypeMajorInt, 2),
+ ComposeDataType(DataTypeMajorInt, 2),
pos,
3)
s.requireEncodeAndDecodeDecimalNoError(
- composeDataType(DataTypeMajorInt, 2),
+ ComposeDataType(DataTypeMajorInt, 2),
zero,
3)
s.requireEncodeAndDecodeDecimalNoError(
- composeDataType(DataTypeMajorInt, 2),
+ ComposeDataType(DataTypeMajorInt, 2),
neg,
3)
s.requireEncodeAndDecodeDecimalNoError(
- composeDataType(DataTypeMajorUint, 2),
+ ComposeDataType(DataTypeMajorUint, 2),
pos,
3)
s.requireEncodeAndDecodeDecimalNoError(
- composeDataType(DataTypeMajorUint, 2),
+ ComposeDataType(DataTypeMajorUint, 2),
zero,
3)
s.requireEncodeAndDecodeDecimalNoError(
- composeDataType(DataTypeMajorAddress, 0),
+ ComposeDataType(DataTypeMajorAddress, 0),
pos,
20)
s.requireEncodeAndDecodeDecimalNoError(
- composeDataType(DataTypeMajorAddress, 0),
+ ComposeDataType(DataTypeMajorAddress, 0),
zero,
20)
@@ -130,24 +130,24 @@ func (s *TypesTestSuite) TestEncodeAndDecodeDecimal() {
neg = decimal.New(-15, -2)
s.requireEncodeAndDecodeDecimalNoError(
- composeDataType(DataTypeMajorFixed+2, 2),
+ ComposeDataType(DataTypeMajorFixed+2, 2),
pos,
3)
s.requireEncodeAndDecodeDecimalNoError(
- composeDataType(DataTypeMajorFixed+2, 2),
+ ComposeDataType(DataTypeMajorFixed+2, 2),
zero,
3)
s.requireEncodeAndDecodeDecimalNoError(
- composeDataType(DataTypeMajorFixed+2, 2),
+ ComposeDataType(DataTypeMajorFixed+2, 2),
neg,
3)
s.requireEncodeAndDecodeDecimalNoError(
- composeDataType(DataTypeMajorUfixed+2, 2),
+ ComposeDataType(DataTypeMajorUfixed+2, 2),
pos,
3)
s.requireEncodeAndDecodeDecimalNoError(
- composeDataType(DataTypeMajorUfixed+2, 2),
+ ComposeDataType(DataTypeMajorUfixed+2, 2),
zero,
3)
}
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))
+}