You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
266 lines
4.1 KiB
266 lines
4.1 KiB
5 years ago
|
// Package querytext is the old query parser and parameter substitute process.
|
||
|
// Do not use on new code.
|
||
|
//
|
||
|
// This package is not subject to any API compatibility guarantee.
|
||
|
package querytext
|
||
8 years ago
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"io"
|
||
|
"strconv"
|
||
|
)
|
||
|
|
||
|
type parser struct {
|
||
|
r *bytes.Reader
|
||
|
w bytes.Buffer
|
||
|
paramCount int
|
||
|
paramMax int
|
||
5 years ago
|
|
||
|
// using map as a set
|
||
|
namedParams map[string]bool
|
||
8 years ago
|
}
|
||
|
|
||
|
func (p *parser) next() (rune, bool) {
|
||
|
ch, _, err := p.r.ReadRune()
|
||
|
if err != nil {
|
||
|
if err != io.EOF {
|
||
|
panic(err)
|
||
|
}
|
||
|
return 0, false
|
||
|
}
|
||
|
return ch, true
|
||
|
}
|
||
|
|
||
|
func (p *parser) unread() {
|
||
|
err := p.r.UnreadRune()
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (p *parser) write(ch rune) {
|
||
|
p.w.WriteRune(ch)
|
||
|
}
|
||
|
|
||
|
type stateFunc func(*parser) stateFunc
|
||
|
|
||
5 years ago
|
// ParseParams rewrites the query from using "?" placeholders
|
||
|
// to using "@pN" parameter names that SQL Server will accept.
|
||
|
//
|
||
|
// This function and package is not subject to any API compatibility guarantee.
|
||
|
func ParseParams(query string) (string, int) {
|
||
8 years ago
|
p := &parser{
|
||
5 years ago
|
r: bytes.NewReader([]byte(query)),
|
||
|
namedParams: map[string]bool{},
|
||
8 years ago
|
}
|
||
|
state := parseNormal
|
||
|
for state != nil {
|
||
|
state = state(p)
|
||
|
}
|
||
5 years ago
|
return p.w.String(), p.paramMax + len(p.namedParams)
|
||
8 years ago
|
}
|
||
|
|
||
|
func parseNormal(p *parser) stateFunc {
|
||
|
for {
|
||
|
ch, ok := p.next()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
if ch == '?' {
|
||
5 years ago
|
return parseOrdinalParameter
|
||
8 years ago
|
} else if ch == '$' || ch == ':' {
|
||
|
ch2, ok := p.next()
|
||
|
if !ok {
|
||
|
p.write(ch)
|
||
|
return nil
|
||
|
}
|
||
|
p.unread()
|
||
|
if ch2 >= '0' && ch2 <= '9' {
|
||
5 years ago
|
return parseOrdinalParameter
|
||
|
} else if 'a' <= ch2 && ch2 <= 'z' || 'A' <= ch2 && ch2 <= 'Z' {
|
||
|
return parseNamedParameter
|
||
8 years ago
|
}
|
||
|
}
|
||
|
p.write(ch)
|
||
|
switch ch {
|
||
|
case '\'':
|
||
|
return parseQuote
|
||
|
case '"':
|
||
|
return parseDoubleQuote
|
||
|
case '[':
|
||
|
return parseBracket
|
||
|
case '-':
|
||
|
return parseLineComment
|
||
|
case '/':
|
||
|
return parseComment
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
5 years ago
|
func parseOrdinalParameter(p *parser) stateFunc {
|
||
8 years ago
|
var paramN int
|
||
|
var ok bool
|
||
|
for {
|
||
|
var ch rune
|
||
|
ch, ok = p.next()
|
||
|
if ok && ch >= '0' && ch <= '9' {
|
||
|
paramN = paramN*10 + int(ch-'0')
|
||
|
} else {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if ok {
|
||
|
p.unread()
|
||
|
}
|
||
|
if paramN == 0 {
|
||
|
p.paramCount++
|
||
|
paramN = p.paramCount
|
||
|
}
|
||
|
if paramN > p.paramMax {
|
||
|
p.paramMax = paramN
|
||
|
}
|
||
|
p.w.WriteString("@p")
|
||
|
p.w.WriteString(strconv.Itoa(paramN))
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
return parseNormal
|
||
|
}
|
||
|
|
||
5 years ago
|
func parseNamedParameter(p *parser) stateFunc {
|
||
|
var paramName string
|
||
|
var ok bool
|
||
|
for {
|
||
|
var ch rune
|
||
|
ch, ok = p.next()
|
||
|
if ok && (ch >= '0' && ch <= '9' || 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z') {
|
||
|
paramName = paramName + string(ch)
|
||
|
} else {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if ok {
|
||
|
p.unread()
|
||
|
}
|
||
|
p.namedParams[paramName] = true
|
||
|
p.w.WriteString("@")
|
||
|
p.w.WriteString(paramName)
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
return parseNormal
|
||
|
}
|
||
|
|
||
8 years ago
|
func parseQuote(p *parser) stateFunc {
|
||
|
for {
|
||
|
ch, ok := p.next()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
p.write(ch)
|
||
|
if ch == '\'' {
|
||
|
return parseNormal
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func parseDoubleQuote(p *parser) stateFunc {
|
||
|
for {
|
||
|
ch, ok := p.next()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
p.write(ch)
|
||
|
if ch == '"' {
|
||
|
return parseNormal
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func parseBracket(p *parser) stateFunc {
|
||
|
for {
|
||
|
ch, ok := p.next()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
p.write(ch)
|
||
|
if ch == ']' {
|
||
|
ch, ok = p.next()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
if ch != ']' {
|
||
|
p.unread()
|
||
|
return parseNormal
|
||
|
}
|
||
|
p.write(ch)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func parseLineComment(p *parser) stateFunc {
|
||
|
ch, ok := p.next()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
if ch != '-' {
|
||
|
p.unread()
|
||
|
return parseNormal
|
||
|
}
|
||
|
p.write(ch)
|
||
|
for {
|
||
|
ch, ok = p.next()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
p.write(ch)
|
||
|
if ch == '\n' {
|
||
|
return parseNormal
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func parseComment(p *parser) stateFunc {
|
||
|
var nested int
|
||
|
ch, ok := p.next()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
if ch != '*' {
|
||
|
p.unread()
|
||
|
return parseNormal
|
||
|
}
|
||
|
p.write(ch)
|
||
|
for {
|
||
|
ch, ok = p.next()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
p.write(ch)
|
||
|
for ch == '*' {
|
||
|
ch, ok = p.next()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
p.write(ch)
|
||
|
if ch == '/' {
|
||
|
if nested == 0 {
|
||
|
return parseNormal
|
||
|
} else {
|
||
|
nested--
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for ch == '/' {
|
||
|
ch, ok = p.next()
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
p.write(ch)
|
||
|
if ch == '*' {
|
||
|
nested++
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|