diff options
author | Ting-Wei Lan <tingwei.lan@cobinhood.com> | 2019-04-25 16:17:06 +0800 |
---|---|---|
committer | Ting-Wei Lan <tingwei.lan@cobinhood.com> | 2019-05-14 11:04:15 +0800 |
commit | 06266ab9f5f5491fed858a1cee668d37d2b3a44c (patch) | |
tree | 73504a8940f2da100a31357a5b57233723fbfb77 /core | |
parent | 9f277bb0d0ebdff13dc77e9b8e83a1dee93d6a7b (diff) | |
download | dexon-06266ab9f5f5491fed858a1cee668d37d2b3a44c.tar dexon-06266ab9f5f5491fed858a1cee668d37d2b3a44c.tar.gz dexon-06266ab9f5f5491fed858a1cee668d37d2b3a44c.tar.bz2 dexon-06266ab9f5f5491fed858a1cee668d37d2b3a44c.tar.lz dexon-06266ab9f5f5491fed858a1cee668d37d2b3a44c.tar.xz dexon-06266ab9f5f5491fed858a1cee668d37d2b3a44c.tar.zst dexon-06266ab9f5f5491fed858a1cee668d37d2b3a44c.zip |
code backup 20
Diffstat (limited to 'core')
-rw-r--r-- | core/vm/sqlvm/checker/actions.go (renamed from core/vm/sqlvm/checkers/actions.go) | 2 | ||||
-rw-r--r-- | core/vm/sqlvm/checker/checker.go (renamed from core/vm/sqlvm/checkers/checkers.go) | 189 | ||||
-rw-r--r-- | core/vm/sqlvm/checker/utils.go (renamed from core/vm/sqlvm/checkers/utils.go) | 24 | ||||
-rw-r--r-- | core/vm/sqlvm/cmd/ast-checker/main.go | 45 |
4 files changed, 212 insertions, 48 deletions
diff --git a/core/vm/sqlvm/checkers/actions.go b/core/vm/sqlvm/checker/actions.go index c15029d9a..6adb9b912 100644 --- a/core/vm/sqlvm/checkers/actions.go +++ b/core/vm/sqlvm/checker/actions.go @@ -1,4 +1,4 @@ -package checkers +package checker import ( "fmt" diff --git a/core/vm/sqlvm/checkers/checkers.go b/core/vm/sqlvm/checker/checker.go index 7881aa149..dbdad68fa 100644 --- a/core/vm/sqlvm/checkers/checkers.go +++ b/core/vm/sqlvm/checker/checker.go @@ -1,4 +1,4 @@ -package checkers +package checker import ( "fmt" @@ -841,7 +841,7 @@ func checkExpr(n ast.ExprNode, return checkPosOperator(n, s, o, c, el, tr, ta) case *ast.NegOperatorNode: - return n + return checkNegOperator(n, s, o, c, el, tr, ta) case *ast.NotOperatorNode: return n @@ -947,7 +947,7 @@ func checkVariable(n *ast.IdentifierNode, } cn := string(n.Name) - cd, found := c.FindColumnInBase(tr, cn) + cd, found := c.FindColumnInBaseWithFallback(tr, cn, s) if !found { el.Append(errors.Error{ Position: n.GetPosition(), @@ -1492,8 +1492,7 @@ func elAppendTypeErrorOperatorValueNode(el *errors.ErrorList, n ast.Valuer, } func elAppendTypeErrorOperatorDataType(el *errors.ErrorList, n ast.ExprNode, - fn string, op string) { - dt := n.GetType() + fn string, op string, dt ast.DataType) { el.Append(errors.Error{ Position: n.GetPosition(), Length: n.GetLength(), @@ -1518,23 +1517,56 @@ func checkPosOperator(n *ast.PosOperatorNode, if target == nil { return nil } + r := ast.ExprNode(target) - if v, ok := target.(ast.Valuer); ok { - switch v := v.(type) { + dtTarget := target.GetType() + if !dtTarget.Pending() { + major, _ := ast.DecomposeDataType(dtTarget) + switch { + case major == ast.DataTypeMajorInt, + major == ast.DataTypeMajorUint, + major.IsFixedRange(), + major.IsUfixedRange(): + default: + elAppendTypeErrorOperatorDataType(el, target, fn, op, dtTarget) + return nil + } + } + dt := dtTarget + + if target, ok := target.(ast.Valuer); ok { + switch v := target.(type) { case *ast.IntegerValueNode: - // Clone the node to reset IsAddress to false. - if v.IsAddress { - result := &ast.IntegerValueNode{} - result.SetPosition(v.GetPosition()) - result.SetLength(v.GetLength()) - result.SetToken(v.GetToken()) - result.SetType(v.GetType()) - result.IsAddress = false - result.V = v.V - target = result - } + node := &ast.IntegerValueNode{} + node.SetPosition(n.GetPosition()) + node.SetLength(n.GetLength()) + node.SetToken(n.GetToken()) + node.SetType(dt) + node.IsAddress = false + node.V = v.V + r = node + case *ast.DecimalValueNode: - // Do nothing because the result is the same as the input. + node := &ast.DecimalValueNode{} + node.SetPosition(n.GetPosition()) + node.SetLength(n.GetLength()) + node.SetToken(n.GetToken()) + node.SetType(dt) + node.V = v.V + r = node + + case *ast.NullValueNode: + if dt.Pending() { + elAppendTypeErrorOperatorValueNode(el, v, fn, op) + return nil + } + node := &ast.NullValueNode{} + node.SetPosition(n.GetPosition()) + node.SetLength(n.GetLength()) + node.SetToken(n.GetToken()) + node.SetType(dt) + r = node + case *ast.BoolValueNode: elAppendTypeErrorOperatorValueNode(el, v, fn, op) return nil @@ -1544,36 +1576,139 @@ func checkPosOperator(n *ast.PosOperatorNode, case *ast.BytesValueNode: elAppendTypeErrorOperatorValueNode(el, v, fn, op) return nil - case *ast.NullValueNode: - elAppendTypeErrorOperatorValueNode(el, v, fn, op) - return nil default: panic(unknownValueNodeType(v)) } } - dt := target.GetType() if dt.Pending() { - target = checkExpr(target, s, o, c, el, tr, ta) + r = checkExpr(r, s, o, c, el, tr, ta) } else { - major, _ := ast.DecomposeDataType(dt) + switch a := ta.(type) { + case typeActionInferDefault: + case typeActionInferWithSize: + case typeActionAssign: + if !dt.Equal(a.dt) { + elAppendTypeErrorMismatch(el, n, fn, a.dt, dt) + return nil + } + } + } + return r +} + +func checkNegOperator(n *ast.NegOperatorNode, + s schema.Schema, o CheckOptions, c *schemaCache, el *errors.ErrorList, + tr schema.TableRef, ta typeAction) ast.ExprNode { + + fn := "CheckNegOperator" + op := "unary operator -" + + target := n.GetTarget() + target = checkExpr(target, s, o, c, el, tr, nil) + if target == nil { + return nil + } + n.SetTarget(target) + r := ast.ExprNode(n) + + dtTarget := target.GetType() + if !dtTarget.Pending() { + major, _ := ast.DecomposeDataType(dtTarget) switch { case major == ast.DataTypeMajorInt, major == ast.DataTypeMajorUint, major.IsFixedRange(), major.IsUfixedRange(): default: - elAppendTypeErrorOperatorDataType(el, target, fn, op) + elAppendTypeErrorOperatorDataType(el, target, fn, op, dtTarget) return nil } + } + dt := dtTarget + + eval := func(n ast.Valuer, v decimal.Decimal) (decimal.Decimal, bool) { + r := v.Neg() + if !dt.Pending() { + min, max := mustGetMinMax(dt) + if r.LessThan(min) || r.GreaterThan(max) { + if (o & CheckWithSafeMath) != 0 { + elAppendOverflowError(el, n, fn, dt, r, min, max) + return r, false + } + cropped := cropDecimal(dt, r) + elAppendOverflowWarning(el, n, fn, dt, r, cropped) + r = cropped + } + } + normalizeDecimal(&r) + return r, true + } + if target, ok := target.(ast.Valuer); ok { + switch v := target.(type) { + case *ast.IntegerValueNode: + node := &ast.IntegerValueNode{} + node.SetPosition(n.GetPosition()) + node.SetLength(n.GetLength()) + node.SetToken(n.GetToken()) + node.SetType(dt) + node.IsAddress = false + node.V, ok = eval(node, v.V) + if !ok { + return nil + } + r = node + + case *ast.DecimalValueNode: + node := &ast.DecimalValueNode{} + node.SetPosition(n.GetPosition()) + node.SetLength(n.GetLength()) + node.SetToken(n.GetToken()) + node.SetType(dt) + node.V, ok = eval(node, v.V) + if !ok { + return nil + } + r = node + + case *ast.NullValueNode: + if dt.Pending() { + elAppendTypeErrorOperatorValueNode(el, v, fn, op) + return nil + } + node := &ast.NullValueNode{} + node.SetPosition(n.GetPosition()) + node.SetLength(n.GetLength()) + node.SetToken(n.GetToken()) + node.SetType(dt) + r = node + + case *ast.BoolValueNode: + elAppendTypeErrorOperatorValueNode(el, v, fn, op) + return nil + case *ast.AddressValueNode: + elAppendTypeErrorOperatorValueNode(el, v, fn, op) + return nil + case *ast.BytesValueNode: + elAppendTypeErrorOperatorValueNode(el, v, fn, op) + return nil + default: + panic(unknownValueNodeType(v)) + } + } + + if dt.Pending() { + r = checkExpr(r, s, o, c, el, tr, ta) + } else { switch a := ta.(type) { case typeActionInferDefault: case typeActionInferWithSize: case typeActionAssign: if !dt.Equal(a.dt) { elAppendTypeErrorMismatch(el, n, fn, a.dt, dt) + return nil } } } - return target + return r } diff --git a/core/vm/sqlvm/checkers/utils.go b/core/vm/sqlvm/checker/utils.go index 4a8bbd96e..34b73af4a 100644 --- a/core/vm/sqlvm/checkers/utils.go +++ b/core/vm/sqlvm/checker/utils.go @@ -1,4 +1,4 @@ -package checkers +package checker import ( "github.com/dexon-foundation/decimal" @@ -52,6 +52,9 @@ func safeDecimalRange(d decimal.Decimal) bool { return d.GreaterThanOrEqual(MinConstant) && d.LessThanOrEqual(MaxConstant) } +// schemaCache is a multi-layer symbol table used to support the checker. +// It allows changes to be easily rolled back by keeping modifications in a +// separate layer, providing an experience similar to a database transaction. type schemaCache struct { base schemaCacheBase scopes []schemaCacheScope @@ -401,6 +404,8 @@ func (c *schemaCache) DeleteColumn(tr schema.TableRef, n string) bool { return true } +// columnRefSlice implements sort.Interface. It allows sorting a slice of +// schema.ColumnRef while keeping references to AST nodes they originate from. type columnRefSlice struct { columns []schema.ColumnRef nodes []uint8 @@ -431,11 +436,20 @@ func (s columnRefSlice) Swap(i, j int) { s.nodes[i], s.nodes[j] = s.nodes[j], s.nodes[i] } +// typeAction represents an action on type inference requested from the parent +// node. An action is usually only applied on a single node. It is seldom +// propagated to child nodes because we want to delay the assignment of types +// until it is necessary, making constant operations easier to use without +// being restricted by data types. //go-sumtype:decl typeAction type typeAction interface { ˉtypeAction() } +// typeActionInferDefault requests the node to infer the type using its default +// rule. It usually means that the parent node does not care the data type, +// such as the select list in a SELECT statement. It is an advisory request. +// If the type of the node is already determined, it should ignore the request. type typeActionInferDefault struct{} func newTypeActionInferDefaultSize() typeActionInferDefault { @@ -446,6 +460,11 @@ var _ typeAction = typeActionInferDefault{} func (typeActionInferDefault) ˉtypeAction() {} +// typeActionInferWithSize requests the node to infer the type with size +// requirement. The size is measured in bytes. It is indented to be used in +// CAST to support conversion between integer and fixed-size bytes types. +// It is an advisory request. If the type is already determined, the request is +// ignored and the parent node should be able to handle the problem by itself. type typeActionInferWithSize struct { size int } @@ -462,6 +481,9 @@ type typeActionAssign struct { dt ast.DataType } +// newTypeActionAssign requests the node to have a specific type. It is a +// mandatory request. If the node is unable to meet the requirement, it should +// throw an error. It is not allowed to ignore the request. func newTypeActionAssign(expected ast.DataType) typeActionAssign { return typeActionAssign{dt: expected} } diff --git a/core/vm/sqlvm/cmd/ast-checker/main.go b/core/vm/sqlvm/cmd/ast-checker/main.go index c02b58f0f..1c90d5028 100644 --- a/core/vm/sqlvm/cmd/ast-checker/main.go +++ b/core/vm/sqlvm/cmd/ast-checker/main.go @@ -7,18 +7,18 @@ import ( "fmt" "os" - "github.com/dexon-foundation/dexon/core/vm/sqlvm/checkers" + "github.com/dexon-foundation/dexon/core/vm/sqlvm/checker" "github.com/dexon-foundation/dexon/core/vm/sqlvm/parser" "github.com/dexon-foundation/dexon/core/vm/sqlvm/schema" "github.com/dexon-foundation/dexon/rlp" ) -func create(sql string, o checkers.CheckOptions) int { +func create(sql string, o checker.CheckOptions) int { n, parseErr := parser.Parse([]byte(sql)) if parseErr != nil { fmt.Fprintf(os.Stderr, "Parse error:\n%+v\n", parseErr) } - s, checkErr := checkers.CheckCreate(n, o) + s, checkErr := checker.CheckCreate(n, o) if checkErr != nil { fmt.Fprintf(os.Stderr, "Check error:\n%+v\n", checkErr) } @@ -52,42 +52,49 @@ func decode(ss string) int { return 0 } -func query(ss, sql string, o checkers.CheckOptions) int { +func query(ss, sql string, o checker.CheckOptions) int { fmt.Fprintln(os.Stderr, "Function not implemented") return 1 } -func exec(ss, sql string, o checkers.CheckOptions) int { +func exec(ss, sql string, o checker.CheckOptions) int { fmt.Fprintln(os.Stderr, "Function not implemented") return 1 } func main() { - var noSafeMath bool - var noSafeCast bool - flag.BoolVar(&noSafeMath, "no-safe-math", false, "disable safe math") - flag.BoolVar(&noSafeCast, "no-safe-cast", false, "disable safe cast") + var safeMath bool + var safeCast bool + var constantOnly bool + flag.BoolVar(&safeMath, "safe-math", true, "") + flag.BoolVar(&safeCast, "safe-cast", true, "") + flag.BoolVar(&constantOnly, "constant-only", false, " (default false)") flag.Parse() if flag.NArg() < 1 { fmt.Fprintf(os.Stderr, - "Usage: %s <action> <arguments>\n"+ + "Usage: %s [options] <action> <arguments>\n"+ + "Options:\n"+ + " -help Show options\n"+ "Actions:\n"+ - " create <SQL> returns schema\n"+ - " decode <schema> returns SQL\n"+ - " query <schema> <SQL> returns AST\n"+ - " exec <schema> <SQL> returns AST\n", + " create (SQL) -> schema\n"+ + " decode (schema) -> SQL\n"+ + " query (schema, SQL) -> AST\n"+ + " exec (schema, SQL) -> AST\n", os.Args[0]) os.Exit(1) } - o := checkers.CheckWithSafeMath | checkers.CheckWithSafeCast - if noSafeMath { - o &= ^(checkers.CheckWithSafeMath) + var o checker.CheckOptions + if safeMath { + o |= checker.CheckWithSafeMath } - if noSafeCast { - o &= ^(checkers.CheckWithSafeCast) + if safeCast { + o |= checker.CheckWithSafeCast + } + if constantOnly { + o |= checker.CheckWithConstantOnly } action := flag.Arg(0) |