aboutsummaryrefslogtreecommitdiffstats
path: root/core/vm/sqlvm/schema
diff options
context:
space:
mode:
authorwmin0 <wmin0@cobinhood.com>2019-02-12 11:09:34 +0800
committerJhih-Ming Huang <jm.huang@cobinhood.com>2019-03-26 17:48:21 +0800
commit3292a673720e99d56495bc07ff0fc03f325c0737 (patch)
tree2f7c353203d216489ab6b6b0e665e6314427bf6f /core/vm/sqlvm/schema
parent457f0a5a26a4dc97c02d9d442ab7425d53a918a8 (diff)
downloaddexon-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.go139
-rw-r--r--core/vm/sqlvm/schema/schema_test.go127
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))
+}