func TimeExtractFunc(ctx expr.EvalContext, items ...value.Value) (value.StringValue, bool) { switch len(items) { case 0: // if we have no "items", return time associated with ctx // This is an alias of now() t := ctx.Ts() if !t.IsZero() { return value.NewStringValue(t.String()), true } return value.EmptyStringValue, false case 1: // if only 1 item, convert item to time dateStr, ok := value.ToString(items[0].Rv()) if !ok { return value.EmptyStringValue, false } t, err := dateparse.ParseAny(dateStr) if err != nil { return value.EmptyStringValue, false } return value.NewStringValue(t.String()), true case 2: // if we have 2 items, the first is the time string // and the second is the format string. // Use leekchan/timeutil package dateStr, ok := value.ToString(items[0].Rv()) if !ok { return value.EmptyStringValue, false } formatStr, ok := value.ToString(items[1].Rv()) if !ok { return value.EmptyStringValue, false } t, err := dateparse.ParseAny(dateStr) if err != nil { return value.EmptyStringValue, false } formatted := timeutil.Strftime(&t, formatStr) return value.NewStringValue(formatted), true default: return value.EmptyStringValue, false } }
// Extract Domain from a Value, or Values (must be urlish), doesn't do much/any validation // // domain("http://www.lytics.io/index.html") => "lytics.io" // // if input is a list of strings, only first is evaluated, for plural see domains() // func DomainFunc(ctx expr.EvalContext, item value.Value) (value.StringValue, bool) { urlstr := "" switch itemT := item.(type) { case value.StringValue: urlstr = itemT.Val() case value.StringsValue: for _, sv := range itemT.Val() { urlstr = sv break } } urlstr = strings.ToLower(urlstr) if !strings.HasPrefix(urlstr, "http") { urlstr = "http://" + urlstr } if urlParsed, err := url.Parse(urlstr); err == nil { parts := strings.Split(urlParsed.Host, ".") if len(parts) > 2 { parts = parts[len(parts)-2:] } if len(parts) > 0 { return value.NewStringValue(strings.Join(parts, ".")), true } } return value.EmptyStringValue, false }
// String lower function // must be able to convert to string // func Lower(ctx expr.EvalContext, item value.Value) (value.StringValue, bool) { val, ok := value.ToString(item.Rv()) if !ok { return value.EmptyStringValue, false } return value.NewStringValue(strings.ToLower(val)), true }
// Extract host from a String (must be urlish), doesn't do much/any validation func Qs(ctx expr.EvalContext, urlItem, keyItem value.Value) (value.StringValue, bool) { val, ok := value.ToString(urlItem.Rv()) if !ok { return value.EmptyStringValue, false } if val == "" { return value.EmptyStringValue, false } urlstr := strings.ToLower(val) if len(urlstr) < 8 { return value.EmptyStringValue, false } keyVal, ok := value.ToString(keyItem.Rv()) if !ok { return value.EmptyStringValue, false } if keyVal == "" { return value.EmptyStringValue, false } if !strings.HasPrefix(urlstr, "http") { urlstr = "http://" + urlstr } if urlParsed, err := url.Parse(urlstr); err == nil { //u.Infof("url.parse: %#v", urlParsed) qsval, ok := urlParsed.Query()[keyVal] if !ok { return value.EmptyStringValue, false } if len(qsval) > 0 { return value.NewStringValue(qsval[0]), true } } return value.EmptyStringValue, false }
// Extract url path from a String (must be urlish), doesn't do much/any validation // // path("http://www.lytics.io/blog/index.html") => blog/index.html // // In the event the value contains more than one input url, will ONLY evaluate first // func UrlPath(ctx expr.EvalContext, item value.Value) (value.StringValue, bool) { val := "" switch itemT := item.(type) { case value.StringValue: val = itemT.Val() case value.StringsValue: if len(itemT.Val()) == 0 { return value.EmptyStringValue, false } val = itemT.Val()[0] } if val == "" { return value.EmptyStringValue, false } urlstr := strings.ToLower(val) if len(urlstr) < 8 { return value.EmptyStringValue, false } if !strings.HasPrefix(urlstr, "http") { urlstr = "http://" + urlstr } if urlParsed, err := url.Parse(urlstr); err == nil { //u.Infof("url.parse: %#v", urlParsed) return value.NewStringValue(urlParsed.Path), true } return value.EmptyStringValue, false }
// defaultTypeWriter Convert a qlbridge value type to qlbridge value type // func defaultTypeWriter(ctx expr.EvalContext, val value.Value) (value.StringValue, bool) { switch sv := val.(type) { case value.StringValue: return sv, true } return value.NewStringValue(""), false }
// Join items together (string concatenation) // // join("apples","oranges",",") => "apples,oranges" // join(["apples","oranges"],",") => "apples,oranges" // join("apples","oranges","") => "applesoranges" // func JoinFunc(ctx expr.EvalContext, items ...value.Value) (value.StringValue, bool) { if len(items) <= 1 { return value.EmptyStringValue, false } sep, ok := value.ToString(items[len(items)-1].Rv()) if !ok { return value.EmptyStringValue, false } args := make([]string, 0) for i := 0; i < len(items)-1; i++ { switch valTyped := items[i].(type) { case value.SliceValue: vals := make([]string, len(valTyped.Val())) for i, sv := range valTyped.Val() { vals[i] = sv.ToString() } args = append(args, vals...) case value.StringsValue: vals := make([]string, len(valTyped.Val())) for i, sv := range valTyped.Val() { vals[i] = sv } args = append(args, vals...) case value.StringValue, value.NumberValue, value.IntValue: val := valTyped.ToString() if val == "" { return value.EmptyStringValue, false } args = append(args, val) } } return value.NewStringValue(strings.Join(args, sep)), true }
func (m *Sqlbridge) parseUpdateList() (map[string]*ValueColumn, error) { cols := make(map[string]*ValueColumn) lastColName := "" for { //u.Debug(m.Cur().String()) switch m.Cur().T { case lex.TokenWhere, lex.TokenLimit, lex.TokenEOS, lex.TokenEOF: return cols, nil case lex.TokenValue: cols[lastColName] = &ValueColumn{Value: value.NewStringValue(m.Cur().V)} case lex.TokenInteger: iv, _ := strconv.ParseInt(m.Cur().V, 10, 64) cols[lastColName] = &ValueColumn{Value: value.NewIntValue(iv)} case lex.TokenComma, lex.TokenEqual: // don't need to do anything case lex.TokenIdentity: lastColName = m.Cur().V case lex.TokenUdfExpr: tree := NewTree(m.SqlTokenPager) if err := m.parseNode(tree); err != nil { u.Errorf("could not parse: %v", err) return nil, err } cols[lastColName] = &ValueColumn{Expr: tree.Root} default: u.Warnf("don't know how to handle ? %v", m.Cur()) return nil, fmt.Errorf("expected column but got: %v", m.Cur().String()) } m.Next() } panic("unreachable") }
func BenchmarkVmExecute(b *testing.B) { msg := datasource.NewContextSimpleData( map[string]value.Value{ "int5": value.NewIntValue(5), "item_count": value.NewStringValue("5"), "reg_date": value.NewStringValue("2014/11/01"), "user_id": value.NewStringValue("abc")}, ) b.ReportAllocs() b.StartTimer() for i := 0; i < b.N; i++ { for _, sqlText := range bmSql { verifyBenchmarkSql(b, sqlText, msg) } } }
func Eval(ctx expr.EvalContext, arg expr.Node) (value.Value, bool) { //u.Debugf("Eval() node=%T %v", arg, arg) // can we switch to arg.Type() switch argVal := arg.(type) { case *expr.NumberNode: return numberNodeToValue(argVal) case *expr.BinaryNode: return walkBinary(ctx, argVal) case *expr.UnaryNode: return walkUnary(ctx, argVal) case *expr.TriNode: return walkTri(ctx, argVal) case *expr.MultiArgNode: return walkMulti(ctx, argVal) case *expr.FuncNode: return walkFunc(ctx, argVal) case *expr.IdentityNode: return walkIdentity(ctx, argVal) case *expr.StringNode: return value.NewStringValue(argVal.Text), true case nil: return nil, true default: u.Errorf("Unknonwn node type: %T", argVal) panic(ErrUnknownNodeType) } }
// HashSha512Func Hash a value to SHA512 string // // hash.sha512("/blog/index.html") => abc345xyz // func HashSha512Func(ctx expr.EvalContext, arg value.Value) (value.StringValue, bool) { if arg.Err() || arg.Nil() { return value.EmptyStringValue, true } hasher := sha512.New() hasher.Write([]byte(arg.ToString())) return value.NewStringValue(hex.EncodeToString(hasher.Sum(nil))), true }
func TestNamespaces(t *testing.T) { a1 := value.NewStringValue("a1") b1 := value.NewStringValue("b1") b2 := value.NewStringValue("b2") c1 := value.NewStringValue("c1") d1 := value.NewStringValue("d1") readers := []expr.ContextReader{ datasource.NewNamespacedContextReader(datasource.NewContextSimpleData(map[string]value.Value{ "a": a1, "b": b1, "d": d1, }), "foo"), datasource.NewNamespacedContextReader(datasource.NewContextSimpleData(map[string]value.Value{ "b": b2, "c": c1, }), "BAR"), datasource.NewContextSimpleData(map[string]value.Value{ "a": a1, }), } nc := datasource.NewNestedContextReader(readers, time.Now()) expected := map[string]value.Value{ "foo.a": a1, "foo.b": b1, "foo.d": d1, "bar.b": b2, "bar.c": c1, "a": a1, } for k, v := range expected { checkval(t, nc, k, v) } r := nc.Row() assert.Equal(t, len(expected), len(r)) for k, v := range expected { assert.Equal(t, v, r[k]) } // _, ok := nc.Get("no") // assert.Equal(t, false, ok) }
func walkIdentity(ctx expr.EvalContext, node *expr.IdentityNode) (value.Value, bool) { if node.IsBooleanIdentity() { //u.Debugf("walkIdentity() boolean: node=%T %v Bool:%v", node, node, node.Bool()) return value.NewBoolValue(node.Bool()), true } if ctx == nil { return value.NewStringValue(node.Text), true } return ctx.Get(node.Text) }
// Get yymm in 4 digits from argument if supplied, else uses message context ts // func YyMm(ctx expr.EvalContext, items ...value.Value) (value.StringValue, bool) { if len(items) == 0 { if !ctx.Ts().IsZero() { t := ctx.Ts() return value.NewStringValue(t.Format(yymmTimeLayout)), true } } else if len(items) == 1 { dateStr, ok := value.ToString(items[0].Rv()) if !ok { return value.EmptyStringValue, false } //u.Infof("v=%v %v ", v, items[0].Rv()) if t, err := dateparse.ParseAny(dateStr); err == nil { return value.NewStringValue(t.Format(yymmTimeLayout)), true } } return value.EmptyStringValue, false }
func TestSqlDelete(t *testing.T) { db := datasource.NewContextSimple() user1 := map[string]value.Value{ "user_id": value.NewIntValue(5), "item_count": value.NewStringValue("5"), "bval": value.NewBoolValue(true), "bvalf": value.NewBoolValue(false), "reg_date": value.NewStringValue("2014/11/01"), "name": value.NewStringValue("bob")} db.Insert(user1) user2 := map[string]value.Value{ "user_id": value.NewIntValue(6), "item_count": value.NewStringValue("5"), "reg_date": value.NewStringValue("2012/11/01"), "name": value.NewStringValue("allison")} db.Insert(user2) assert.Tf(t, len(db.Rows) == 2, "has 2 users") verifySqlDelete(t, ` DELETE FROM mytable WHERE yy(reg_date) == 14 `, db) assert.Tf(t, len(db.Rows) == 1, "must have 1 rows: %v %v", len(db.Rows), db.Rows) assert.Tf(t, db.Rows[0]["name"].ToString() == "allison", "%v", db.Rows) }
// urlminusqs removes a specific query parameter and its value from a url // // urlminusqs("http://www.lytics.io/?q1=google&q2=123", "q1") => "http://www.lytics.io/?q2=123", true // func UrlMinusQs(ctx expr.EvalContext, urlItem, keyItem value.Value) (value.StringValue, bool) { val := "" switch itemT := urlItem.(type) { case value.StringValue: val = itemT.Val() case value.StringsValue: if len(itemT.Val()) == 0 { return value.EmptyStringValue, false } val = itemT.Val()[0] } if val == "" { return value.EmptyStringValue, false } if !strings.HasPrefix(val, "http") { val = "http://" + val } keyVal, ok := value.ToString(keyItem.Rv()) if !ok { return value.EmptyStringValue, false } if keyVal == "" { return value.EmptyStringValue, false } if up, err := url.Parse(val); err == nil { qsval := up.Query() _, ok := qsval[keyVal] if !ok { return value.NewStringValue(fmt.Sprintf("%s://%s%s?%s", up.Scheme, up.Host, up.Path, up.RawQuery)), true } qsval.Del(keyVal) up.RawQuery = qsval.Encode() if up.RawQuery == "" { return value.NewStringValue(fmt.Sprintf("%s://%s%s", up.Scheme, up.Host, up.Path)), true } return value.NewStringValue(fmt.Sprintf("%s://%s%s?%s", up.Scheme, up.Host, up.Path, up.RawQuery)), true } return value.EmptyStringValue, false }
// ValueArray // IN ("a","b","c") // ["a","b","c"] func ValueArray(pg TokenPager) (value.Value, error) { //u.Debugf("valueArray cur:%v peek:%v", pg.Cur().V, pg.Peek().V) vals := make([]value.Value, 0) arrayLoop: for { tok := pg.Next() // consume token //u.Infof("valueArray() consumed token?: %v", tok) switch tok.T { case lex.TokenComma: // continue case lex.TokenRightParenthesis: //u.Warnf("found right paren %v cur: %v", tok, pg.Cur()) break arrayLoop case lex.TokenEOF, lex.TokenEOS, lex.TokenFrom, lex.TokenAs: //u.Debugf("return: %v", tok) break arrayLoop case lex.TokenValue: vals = append(vals, value.NewStringValue(tok.V)) case lex.TokenInteger: fv, err := strconv.ParseFloat(tok.V, 64) if err == nil { vals = append(vals, value.NewNumberValue(fv)) } else { return value.NilValueVal, err } case lex.TokenFloat: fv, err := strconv.ParseFloat(tok.V, 64) if err == nil { vals = append(vals, value.NewNumberValue(fv)) } else { return value.NilValueVal, err } default: return value.NilValueVal, fmt.Errorf("Could not recognize token: %v", tok) } tok = pg.Next() switch tok.T { case lex.TokenComma: // fine, consume the comma case lex.TokenRightBracket: //u.Warnf("right bracket: %v", tok) break arrayLoop default: u.Warnf("unrecognized token: %v", tok) return value.NilValueVal, fmt.Errorf("unrecognized token %v", tok) } } //u.Debugf("returning array: %v", vals) return value.NewSliceValues(vals), nil }
func BenchmarkVmExecuteNoParse(b *testing.B) { readContext := datasource.NewContextSimpleData( map[string]value.Value{ "int5": value.NewIntValue(5), "item_count": value.NewStringValue("5"), "reg_date": value.NewStringValue("2014/11/01"), "user_id": value.NewStringValue("abc")}, ) sqlVm, err := NewSqlVm(bmSql[0]) if err != nil { b.Fail() } writeContext := datasource.NewContextSimple() b.ReportAllocs() b.StartTimer() for i := 0; i < b.N; i++ { err = sqlVm.Execute(writeContext, readContext) if err != nil { b.Fail() } } }
// Replace a string(s), accepts any number of parameters to replace // replaces with "" // // replace("/blog/index.html", "/blog","") => /index.html // replace("/blog/index.html", "/blog") => /index.html // replace("/blog/index.html", "/blog/archive/","/blog") => /blog/index.html // replace(item, "M") // func Replace(ctx expr.EvalContext, vals ...value.Value) (value.StringValue, bool) { if len(vals) < 2 { return value.EmptyStringValue, false } val1 := vals[0].ToString() arg := vals[1] replaceWith := "" if len(vals) == 3 { replaceWith = vals[2].ToString() } if arg.Err() { return value.EmptyStringValue, false } val1 = strings.Replace(val1, arg.ToString(), replaceWith, -1) return value.NewStringValue(val1), true }
// emailname a string, parses email // // emailname("Bob <*****@*****.**>") => Bob // func EmailNameFunc(ctx expr.EvalContext, item value.Value) (value.StringValue, bool) { val, ok := value.ToString(item.Rv()) if !ok { return value.EmptyStringValue, false } if val == "" { return value.EmptyStringValue, false } if len(val) < 6 { return value.EmptyStringValue, false } if em, err := mail.ParseAddress(val); err == nil { return value.NewStringValue(em.Name), true } return value.EmptyStringValue, false }
// Replace a string(s), accepts any number of parameters to replace // replaces with "" // // replace("/blog/index.html", "/blog","M2") => /index.html // replace(item, "M") // func Replace(ctx expr.EvalContext, vals ...value.Value) (value.StringValue, bool) { if len(vals) < 2 { return value.EmptyStringValue, false } val1 := vals[0].ToString() for _, v := range vals[1:] { if v.Err() || v.Nil() { return value.EmptyStringValue, false } else if value.IsNilIsh(v.Rv()) { return value.EmptyStringValue, false } v2 := v.ToString() if len(v2) > 0 { val1 = strings.Replace(val1, v2, "", -1) } } return value.NewStringValue(val1), true }
func Eval(ctx expr.EvalContext, arg expr.Node) (value.Value, bool) { //u.Debugf("Eval() node=%T %v", arg, arg) // can we switch to arg.Type() switch argVal := arg.(type) { case *expr.NumberNode: return numberNodeToValue(argVal) case *expr.BinaryNode: return walkBinary(ctx, argVal) case *expr.UnaryNode: return walkUnary(ctx, argVal) case *expr.TriNode: return walkTri(ctx, argVal) case *expr.ArrayNode: return walkArray(ctx, argVal) case *expr.FuncNode: return walkFunc(ctx, argVal) case *expr.IdentityNode: return walkIdentity(ctx, argVal) case *expr.StringNode: return value.NewStringValue(argVal.Text), true case nil: return nil, false case *expr.NullNode: // WHERE (`users.user_id` != NULL) return value.NewNilValue(), true case *expr.ValueNode: if argVal.Value == nil { return nil, false } switch val := argVal.Value.(type) { case *value.NilValue, value.NilValue: return nil, false case value.SliceValue: //u.Warnf("got slice? %#v", argVal) return val, true } u.Errorf("Unknonwn node type: %#v", argVal.Value) panic(ErrUnknownNodeType) default: u.Errorf("Unknonwn node type: %#v", arg) panic(ErrUnknownNodeType) } }
// urlmain remove the querystring and scheme from url // // urlmain("http://www.lytics.io/?utm_source=google") => "www.lytics.io/", true // func UrlMain(ctx expr.EvalContext, urlItem value.Value) (value.StringValue, bool) { val := "" switch itemT := urlItem.(type) { case value.StringValue: val = itemT.Val() case value.StringsValue: if len(itemT.Val()) == 0 { return value.EmptyStringValue, false } val = itemT.Val()[0] } if val == "" { return value.EmptyStringValue, false } if up, err := url.Parse(val); err == nil { return value.NewStringValue(fmt.Sprintf("%s%s", up.Host, up.Path)), true } return value.EmptyStringValue, false }
// Join items // // join("applies","oranges",",") => "apples,oranges" // func JoinFunc(ctx expr.EvalContext, items ...value.Value) (value.StringValue, bool) { if len(items) <= 1 { return value.EmptyStringValue, false } sep, ok := value.ToString(items[len(items)-1].Rv()) if !ok { return value.EmptyStringValue, false } args := make([]string, 0) for i := 0; i < len(items)-1; i++ { val, ok := value.ToString(items[i].Rv()) if !ok { return value.EmptyStringValue, false } if val == "" { return value.EmptyStringValue, false } args = append(args, val) } return value.NewStringValue(strings.Join(args, sep)), true }
// email a string, parses email // // email("Bob <*****@*****.**>") => [email protected] // func EmailDomainFunc(ctx expr.EvalContext, item value.Value) (value.StringValue, bool) { val, ok := value.ToString(item.Rv()) if !ok { return value.EmptyStringValue, false } if val == "" { return value.EmptyStringValue, false } if len(val) < 6 { return value.EmptyStringValue, false } if em, err := mail.ParseAddress(strings.ToLower(val)); err == nil { parts := strings.SplitN(strings.ToLower(em.Address), "@", 2) if len(parts) == 2 { return value.NewStringValue(parts[1]), true } } return value.EmptyStringValue, false }
// Extract url path from a String (must be urlish), doesn't do much/any validation func UrlPath(ctx expr.EvalContext, item value.Value) (value.StringValue, bool) { val, ok := value.ToString(item.Rv()) if !ok { return value.EmptyStringValue, false } if val == "" { return value.EmptyStringValue, false } urlstr := strings.ToLower(val) if len(urlstr) < 8 { return value.EmptyStringValue, false } if !strings.HasPrefix(urlstr, "http") { urlstr = "http://" + urlstr } if urlParsed, err := url.Parse(urlstr); err == nil { //u.Infof("url.parse: %#v", urlParsed) return value.NewStringValue(urlParsed.Path), true } return value.EmptyStringValue, false }
func (m *Sqlbridge) parseValueList(stmt *SqlInsert) error { if m.Cur().T != lex.TokenLeftParenthesis { return fmt.Errorf("Expecting opening paren ( but got %v", m.Cur()) } //m.Next() stmt.Rows = make([][]value.Value, 0) var row []value.Value for { //u.Debug(m.Cur().String()) switch m.Cur().T { case lex.TokenLeftParenthesis: // start of row row = make([]value.Value, 0) case lex.TokenRightParenthesis: stmt.Rows = append(stmt.Rows, row) case lex.TokenFrom, lex.TokenInto, lex.TokenLimit, lex.TokenEOS, lex.TokenEOF: // This indicates we have come to the End of the values //u.Debugf("Ending %v ", m.Cur()) return nil case lex.TokenValue: row = append(row, value.NewStringValue(m.Cur().V)) case lex.TokenInteger: iv, _ := strconv.ParseInt(m.Cur().V, 10, 64) row = append(row, value.NewIntValue(iv)) case lex.TokenComma: //row = append(row, col) //u.Debugf("comma, added cols: %v", len(stmt.Columns)) default: u.Warnf("don't know how to handle ? %v", m.Cur()) return fmt.Errorf("expected column but got: %v", m.Cur().String()) } m.Next() } //u.Debugf("cols: %d", len(stmt.Columns)) return nil }
// url decode a string // // urldecode("http://www.lytics.io/index.html") => http://www.lytics.io // // In the event the value contains more than one input url, will ONLY evaluate first // func UrlDecode(ctx expr.EvalContext, item value.Value) (value.StringValue, bool) { val := "" switch itemT := item.(type) { case value.StringValue: val = itemT.Val() case value.StringsValue: if len(itemT.Val()) == 0 { return value.EmptyStringValue, false } val = itemT.Val()[0] } if val == "" { return value.EmptyStringValue, false } val, err := url.QueryUnescape(val) if err != nil { return value.EmptyStringValue, false } return value.NewStringValue(val), true }
func Evaluator(arg expr.Node) EvaluatorFunc { //u.Debugf("Evaluator() node=%T %v", arg, arg) switch argVal := arg.(type) { case *expr.NumberNode: return func(ctx expr.EvalContext) (value.Value, bool) { return numberNodeToValue(argVal) } case *expr.BinaryNode: return func(ctx expr.EvalContext) (value.Value, bool) { return walkBinary(ctx, argVal) } case *expr.UnaryNode: return func(ctx expr.EvalContext) (value.Value, bool) { return walkUnary(ctx, argVal) } case *expr.FuncNode: return func(ctx expr.EvalContext) (value.Value, bool) { return walkFunc(ctx, argVal) } case *expr.IdentityNode: return func(ctx expr.EvalContext) (value.Value, bool) { return walkIdentity(ctx, argVal) } case *expr.StringNode: return func(ctx expr.EvalContext) (value.Value, bool) { return value.NewStringValue(argVal.Text), true } case *expr.TriNode: return func(ctx expr.EvalContext) (value.Value, bool) { return walkTri(ctx, argVal) } case *expr.ArrayNode: return func(ctx expr.EvalContext) (value.Value, bool) { return walkArray(ctx, argVal) } default: u.Errorf("Unknonwn node type: %T", argVal) panic(ErrUnknownNodeType) } }
func (m *Sqlbridge) parseValueList() ([][]*ValueColumn, error) { if m.Cur().T != lex.TokenLeftParenthesis { return nil, fmt.Errorf("Expecting opening paren ( but got %v", m.Cur()) } var row []*ValueColumn values := make([][]*ValueColumn, 0) for { //u.Debug(m.Cur().String()) switch m.Cur().T { case lex.TokenLeftParenthesis: // start of row if len(row) > 0 { values = append(values, row) } row = make([]*ValueColumn, 0) case lex.TokenRightParenthesis: values = append(values, row) case lex.TokenFrom, lex.TokenInto, lex.TokenLimit, lex.TokenEOS, lex.TokenEOF: if len(row) > 0 { values = append(values, row) } return values, nil case lex.TokenValue: row = append(row, &ValueColumn{Value: value.NewStringValue(m.Cur().V)}) case lex.TokenInteger: iv, err := strconv.ParseInt(m.Cur().V, 10, 64) if err != nil { return nil, err } row = append(row, &ValueColumn{Value: value.NewIntValue(iv)}) case lex.TokenFloat: fv, err := strconv.ParseFloat(m.Cur().V, 64) if err != nil { return nil, err } row = append(row, &ValueColumn{Value: value.NewNumberValue(fv)}) case lex.TokenBool: bv, err := strconv.ParseBool(m.Cur().V) if err != nil { return nil, err } row = append(row, &ValueColumn{Value: value.NewBoolValue(bv)}) case lex.TokenIdentity: // TODO: this is a bug in lexer lv := m.Cur().V if bv, err := strconv.ParseBool(lv); err == nil { row = append(row, &ValueColumn{Value: value.NewBoolValue(bv)}) } else { // error? u.Warnf("Could not figure out how to use: %v", m.Cur()) } case lex.TokenLeftBracket: // an array of values? m.Next() // Consume the [ arrayVal, err := expr.ValueArray(m.SqlTokenPager) if err != nil { return nil, err } //n := NewValueNode(arrayVal) row = append(row, &ValueColumn{Value: arrayVal}) u.Infof("what is token? %v peek:%v", m.Cur(), m.Peek()) //t.Next() case lex.TokenComma: // don't need to do anything case lex.TokenUdfExpr: tree := expr.NewTreeFuncs(m.SqlTokenPager, m.funcs) if err := m.parseNode(tree); err != nil { u.Errorf("could not parse: %v", err) return nil, err } //col.Expr = tree.Root row = append(row, &ValueColumn{Expr: tree.Root}) default: u.Warnf("don't know how to handle ? %v", m.Cur()) return nil, fmt.Errorf("expected column but got: %v", m.Cur().String()) } m.Next() } panic("unreachable") }