mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
217 lines
5 KiB
Go
217 lines
5 KiB
Go
package query
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"tux21b.org/v1/gocql/uuid"
|
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
)
|
|
|
|
var InvalidQueryError = errors.New("Invalid query format.")
|
|
|
|
type QueryParser struct {
|
|
tokens []Token
|
|
pos int
|
|
}
|
|
|
|
/* MASTER
|
|
if len(tokens) > 4 && tokens[2].Type == TYPE_FRAME && tokens[4].Type == TYPE_PROFILE {
|
|
frame_type = tokens[2].Text
|
|
profile_id, err = strconv.ParseUint(tokens[4].Text, 10, 64)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
} else if len(tokens) > 2 && tokens[2].Type == TYPE_FRAME {
|
|
frame_type = tokens[2].Text
|
|
} else if len(tokens) > 2 && tokens[2].Type == TYPE_PROFILE {
|
|
profile_id, err = strconv.ParseUint(tokens[2].Text, 10, 64)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
var filter int //TRAVIS the is for the category
|
|
filter = 0
|
|
bm := db.Bitmap{bitmap_id, frame_type, filter}
|
|
return []QueryInput{&bm}, uint64(profile_id), 0
|
|
*/
|
|
func (self *QueryParser) next() *Token {
|
|
self.pos += 1
|
|
if self.pos > len(self.tokens) {
|
|
return nil
|
|
}
|
|
return &self.tokens[self.pos-1]
|
|
}
|
|
|
|
func (self *QueryParser) peek() *Token {
|
|
token := self.next()
|
|
self.backup()
|
|
return token
|
|
}
|
|
|
|
func (self *QueryParser) backup() {
|
|
self.pos -= 1
|
|
}
|
|
|
|
func (self *QueryParser) Parse() (query *Query, err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
var ok bool
|
|
err, ok = r.(error)
|
|
if !ok {
|
|
err = fmt.Errorf("query: %v", r)
|
|
}
|
|
}
|
|
}()
|
|
var token *Token
|
|
|
|
id := uuid.RandomUUID()
|
|
query = &Query{Id: &id, Subqueries: make([]Query, 0), Args: make(map[string]interface{})}
|
|
|
|
token = self.next()
|
|
if token.Type != TYPE_FUNC {
|
|
return nil, fmt.Errorf("Expected function, found token %v.", token)
|
|
}
|
|
|
|
query.Operation = token.Text
|
|
|
|
token = self.next()
|
|
if token.Type != TYPE_LP {
|
|
return nil, fmt.Errorf("Expected '(', found token %v.", token)
|
|
}
|
|
|
|
ArgLoop:
|
|
for {
|
|
token = self.next()
|
|
if token == nil {
|
|
return nil, fmt.Errorf("Unclosed parentheses!")
|
|
}
|
|
|
|
switch token.Type {
|
|
case TYPE_FUNC:
|
|
self.backup()
|
|
subquery, err := self.Parse()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
query.Subqueries = append(query.Subqueries, *subquery)
|
|
case TYPE_VALUE:
|
|
switch query.Operation {
|
|
case "get":
|
|
switch len(query.Args) {
|
|
case 0:
|
|
i, err := strconv.ParseUint(token.Text, 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Expecting integer id! (%v)", err)
|
|
}
|
|
query.Args["id"] = i
|
|
case 1:
|
|
query.Args["frame"] = token.Text
|
|
default:
|
|
return nil, fmt.Errorf("Unexpected argument! (%v)", token)
|
|
}
|
|
case "set":
|
|
switch len(query.Args) {
|
|
case 0:
|
|
i, err := strconv.ParseUint(token.Text, 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Expecting integer id! (%v)", err)
|
|
}
|
|
query.Args["id"] = i
|
|
case 1:
|
|
query.Args["frame"] = token.Text
|
|
case 2:
|
|
i, err := strconv.ParseUint(token.Text, 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Expecting integer id! (%v)", err)
|
|
}
|
|
query.Args["filter"] = i
|
|
case 3:
|
|
i, err := strconv.ParseUint(token.Text, 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Expecting integer id! (%v)", err)
|
|
}
|
|
query.Args["profile_id"] = i
|
|
default:
|
|
return nil, fmt.Errorf("Unexpected argument! (%v)", token)
|
|
}
|
|
|
|
case "top-n":
|
|
i, err := strconv.Atoi(token.Text)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Expecting integer! (%v)", err)
|
|
}
|
|
query.Args["n"] = i
|
|
default:
|
|
spew.Dump("UNPROCESSED VALUE", token)
|
|
}
|
|
continue
|
|
case TYPE_COMMA:
|
|
continue
|
|
case TYPE_RP:
|
|
break ArgLoop
|
|
case TYPE_KEYWORD:
|
|
var value interface{}
|
|
keyword := token.Text
|
|
token = self.next()
|
|
if token == nil || token.Type != TYPE_EQUALS {
|
|
return nil, fmt.Errorf("Expecting equals sign!")
|
|
}
|
|
token = self.next()
|
|
if token == nil || token.Type != TYPE_VALUE {
|
|
return nil, fmt.Errorf("Expecting value!")
|
|
}
|
|
if keyword == "id" {
|
|
value, err = strconv.ParseUint(token.Text, 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Expecting integer id! (%v)", err)
|
|
}
|
|
} else if keyword == "n" {
|
|
value, err = strconv.Atoi(token.Text)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Expecting integer! (%v)", err)
|
|
}
|
|
} else {
|
|
value = token.Text
|
|
}
|
|
query.Args[keyword] = value
|
|
case TYPE_LB:
|
|
query.Args["ids"] = make([]uint64, 0)
|
|
for {
|
|
token = self.next()
|
|
if token == nil {
|
|
return nil, fmt.Errorf("Unclosed list!")
|
|
}
|
|
switch token.Type {
|
|
case TYPE_COMMA:
|
|
break
|
|
case TYPE_VALUE:
|
|
i, err := strconv.ParseUint(token.Text, 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Expecting integer id! (%v)", err)
|
|
}
|
|
query.Args["ids"] = append(query.Args["ids"].([]uint64), i)
|
|
case TYPE_RB:
|
|
continue ArgLoop
|
|
default:
|
|
return nil, fmt.Errorf("Unexpected token! (%v)", token)
|
|
}
|
|
}
|
|
default:
|
|
spew.Dump("unexpected", token)
|
|
panic(token)
|
|
}
|
|
}
|
|
|
|
if query.Operation == "get" && query.Args["frame"] == nil {
|
|
query.Args["frame"] = "general"
|
|
}
|
|
|
|
return query, nil
|
|
}
|
|
|
|
func Parse(tokens []Token) (*Query, error) {
|
|
parser := QueryParser{tokens, 0}
|
|
return parser.Parse()
|
|
}
|