/
limit_plan.go
139 lines (119 loc) · 2.63 KB
/
limit_plan.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
* Radon
*
* Copyright 2018 The Radon Authors.
* Code is licensed under the GPLv3.
*
*/
package planner
import (
"encoding/json"
"fmt"
"strconv"
"github.com/pkg/errors"
"github.com/xelabs/go-mysqlstack/sqlparser"
"github.com/xelabs/go-mysqlstack/sqlparser/depends/hack"
"github.com/xelabs/go-mysqlstack/xlog"
)
var (
_ Plan = &LimitPlan{}
)
// LimitPlan represents order-by plan.
type LimitPlan struct {
log *xlog.Log
node *sqlparser.Select
rewritten *sqlparser.Limit
Offset int
Limit int
// type
typ PlanType
}
// NewLimitPlan used to create LimitPlan.
func NewLimitPlan(log *xlog.Log, node *sqlparser.Select) *LimitPlan {
return &LimitPlan{
log: log,
node: node,
typ: PlanTypeLimit,
}
}
// analyze used to analyze the 'order by' is at the support level.
func (p *LimitPlan) analyze() error {
node := p.node.Limit
if node == nil {
return nil
}
ok := true
sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch node.(type) {
// Limit clause must be SQLVal type.
case *sqlparser.Limit:
return true, nil
case *sqlparser.SQLVal:
val := node.(*sqlparser.SQLVal)
if val.Type != sqlparser.IntVal {
ok = false
return false, nil
}
return true, nil
default:
ok = false
return false, nil
}
}, node)
if !ok {
return errors.New("unsupported: limit.offset.or.counts.must.be.IntVal")
}
return nil
}
// Build used to build distributed querys.
func (p *LimitPlan) Build() error {
if err := p.analyze(); err != nil {
return err
}
node := p.node.Limit
if node == nil {
return nil
}
if node.Offset != nil {
val := node.Offset.(*sqlparser.SQLVal)
out, err := strconv.ParseInt(hack.String(val.Val), 10, 64)
if err != nil {
return err
}
p.Offset = int(out)
}
if node.Rowcount != nil {
val := node.Rowcount.(*sqlparser.SQLVal)
out, err := strconv.ParseInt(hack.String(val.Val), 10, 64)
if err != nil {
return err
}
p.Limit = int(out)
}
p.rewritten = &sqlparser.Limit{Rowcount: sqlparser.NewIntVal([]byte(fmt.Sprintf("%d", p.Offset+p.Limit)))}
return nil
}
// Type returns the type of the plan.
func (p *LimitPlan) Type() PlanType {
return p.typ
}
// JSON returns the plan info.
func (p *LimitPlan) JSON() string {
bout, err := json.MarshalIndent(p, "", "\t")
if err != nil {
return err.Error()
}
return string(bout)
}
// Children returns the children of the plan.
func (p *LimitPlan) Children() *PlanTree {
return nil
}
// ReWritten used to re-write the limit clause.
func (p *LimitPlan) ReWritten() *sqlparser.Limit {
return p.rewritten
}
// Size returns the memory size.
func (p *LimitPlan) Size() int {
return 0
}