forked from dolthub/driver
-
Notifications
You must be signed in to change notification settings - Fork 0
/
query_splitter.go
115 lines (93 loc) · 1.85 KB
/
query_splitter.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
package embedded
import (
"io"
"strings"
)
var openRunes = map[rune]bool{
'(': true,
'"': true,
'\'': true,
'`': true,
}
// RuneStack is a simple stack of runes
type RuneStack struct {
chars []rune
}
// NewByteStack returns a new RuneStack object
func NewByteStack() *RuneStack {
return &RuneStack{chars: make([]rune, 0, 64)}
}
// Push pushes a new rune on the stack
func (bs *RuneStack) Push(b rune) {
bs.chars = append(bs.chars, b)
}
// Pop takes the top value of the top of the stack and returns it
func (bs *RuneStack) Pop() rune {
l := len(bs.chars)
if l == 0 {
return 0
}
ch := bs.chars[l-1]
bs.chars = bs.chars[:l-1]
return ch
}
// Peek returns the value at the top of the stack
func (bs *RuneStack) Peek() rune {
l := len(bs.chars)
if l == 0 {
return 0
}
return bs.chars[l-1]
}
type QuerySplitter struct {
queries string
pos int
}
func NewQuerySplitter(str string) *QuerySplitter {
return &QuerySplitter{
queries: str,
pos: 0,
}
}
func (qs *QuerySplitter) Next() (string, error) {
if qs.pos >= len(qs.queries) {
return "", io.EOF
}
n, err := parseNext(qs.queries[qs.pos:])
if err != nil {
return "", err
}
nextQuery := strings.TrimSpace(qs.queries[qs.pos : qs.pos+n])
qs.pos += n
return nextQuery, nil
}
func (qs *QuerySplitter) HasMore() bool {
return qs.pos < len(qs.queries)
}
func parseNext(queries string) (int, error) {
openStack := NewByteStack()
var prevCh rune
for pos, ch := range queries {
lastOpen := openStack.Peek()
switch lastOpen {
case 0:
if openRunes[ch] {
openStack.Push(ch)
} else if ch == ';' {
return pos + 1, nil
}
case '"', '\'', '`':
if ch == lastOpen && prevCh != '\\' {
openStack.Pop()
}
case '(':
if ch == ')' {
openStack.Pop()
} else if openRunes[ch] {
openStack.Push(ch)
}
}
prevCh = ch
}
return len(queries), nil
}