WIP named

This commit is contained in:
Michał Matczuk
2017-07-25 11:10:19 +02:00
parent fee479a584
commit a8cbe0454a
2 changed files with 140 additions and 0 deletions

80
named.go Normal file
View File

@@ -0,0 +1,80 @@
package gocqlx
import (
"bytes"
"errors"
"github.com/gocql/gocql"
"strconv"
"unicode"
)
type Queryx struct {
*gocql.Query
names []string
}
// Allow digits and letters in bind params; additionally runes are
// checked against underscores, meaning that bind params can have be
// alphanumeric with underscores. Mind the difference between unicode
// digits and numbers, where '5' is a digit but '五' is not.
var allowedBindRunes = []*unicode.RangeTable{unicode.Letter, unicode.Digit}
// CompileNamedQuery compiles a named query into an unbound query using the
// '?' bindvar and a list of names.
func CompileNamedQuery(qs []byte) (cql string, names []string, err error) {
// guess number of names
n := bytes.Count(qs, []byte(":"))
if n == 0 {
return "", nil, errors.New("expected a named query")
}
names = make([]string, 0, n)
rebound := make([]byte, 0, len(qs))
inName := false
last := len(qs) - 1
name := make([]byte, 0, 10)
for i, b := range qs {
// a ':' while we're in a name is an error
if b == ':' {
// if this is the second ':' in a '::' escape sequence, append a ':'
if inName && i > 0 && qs[i-1] == ':' {
rebound = append(rebound, ':')
inName = false
continue
} else if inName {
err = errors.New("unexpected `:` while reading named param at " + strconv.Itoa(i))
return cql, names, err
}
inName = true
name = []byte{}
// if we're in a name, and this is an allowed character, continue
} else if inName && (unicode.IsOneOf(allowedBindRunes, rune(b)) || b == '_' || b == '.') && i != last {
// append the byte to the name if we are in a name and not on the last byte
name = append(name, b)
// if we're in a name and it's not an allowed character, the name is done
} else if inName {
inName = false
// if this is the final byte of the string and it is part of the name, then
// make sure to add it to the name
if i == last && unicode.IsOneOf(allowedBindRunes, rune(b)) {
name = append(name, b)
}
// add the string representation to the names list
names = append(names, string(name))
// add a proper bindvar for the bindType
rebound = append(rebound, '?')
// add this byte to string unless it was not part of the name
if i != last {
rebound = append(rebound, b)
} else if !unicode.IsOneOf(allowedBindRunes, rune(b)) {
rebound = append(rebound, b)
}
} else {
// this is a normal byte and should just go onto the rebound query
rebound = append(rebound, b)
}
}
return string(rebound), names, err
}