// Get year in integer from date func Yy(ctx expr.EvalContext, items ...value.Value) (value.IntValue, bool) { yy := 0 if len(items) == 0 { if !ctx.Ts().IsZero() { yy = ctx.Ts().Year() } } else if len(items) == 1 { dateStr, ok := value.ToString(items[0].Rv()) if !ok { return value.NewIntValue(0), false } //u.Infof("v=%v %v ", v, item.Rv()) if t, err := dateparse.ParseAny(dateStr); err != nil { return value.NewIntValue(0), false } else { yy = t.Year() } } else { return value.NewIntValue(0), false } if yy >= 2000 { yy = yy - 2000 } else if yy >= 1900 { yy = yy - 1900 } //u.Infof("%v yy = %v", item, yy) return value.NewIntValue(int64(yy)), true }
// Convert to Integer: Best attempt at converting to integer // // toint("5") => 5 // toint("5.75") => 5 // toint("5,555") => 5555 // toint("$5") => 5 // toint("5,555.00") => 5555 // func ToInt(ctx expr.EvalContext, item value.Value) (value.IntValue, bool) { iv, ok := value.ToInt64(reflect.ValueOf(item.Value())) if !ok { return value.NewIntValue(0), false } return value.NewIntValue(iv), true }
// Get year in integer from field, must be able to convert to date // // yy() => 15, true // assuming it is 2015 // yy("2014-03-01") => 14, true // func Yy(ctx expr.EvalContext, items ...value.Value) (value.IntValue, bool) { yy := 0 if len(items) == 0 { if !ctx.Ts().IsZero() { yy = ctx.Ts().Year() } else { // Do we want to use Now()? } } else if len(items) == 1 { //u.Debugf("has 1 items? %#v", items[0].Rv()) dateStr, ok := value.ToString(items[0].Rv()) if !ok { return value.NewIntValue(0), false } //u.Debugf("v=%v %v", dateStr, items[0].Rv()) if t, err := dateparse.ParseAny(dateStr); err != nil { return value.NewIntValue(0), false } else { yy = t.Year() } } else { return value.NewIntValue(0), false } if yy >= 2000 { yy = yy - 2000 } else if yy >= 1900 { yy = yy - 1900 } //u.Debugf("yy = %v", yy) return value.NewIntValue(int64(yy)), true }
// Count func CountFunc(ctx EvalContext, val value.Value) (value.IntValue, bool) { if val.Err() || val.Nil() { return value.NewIntValue(0), false } //u.Infof("??? vals=[%v]", val.Value()) return value.NewIntValue(1), true }
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) }
// Convert to Integer: Best attempt at converting to integer // // toint("5") => 5, true // toint("5.75") => 5, true // toint("5,555") => 5555, true // toint("$5") => 5, true // toint("5,555.00") => 5555, true // func ToInt(ctx expr.EvalContext, item value.Value) (value.IntValue, bool) { switch itemT := item.(type) { case value.TimeValue: iv := itemT.Val().UnixNano() / 1e6 // Milliseconds return value.NewIntValue(iv), true } iv, ok := value.ToInt64(reflect.ValueOf(item.Value())) if !ok { return value.NewIntValue(0), false } return value.NewIntValue(iv), true }
// totimestamp: convert to date, then to unix Seconds // func ToTimestamp(ctx expr.EvalContext, item value.Value) (value.IntValue, bool) { dateStr, ok := value.ToString(item.Rv()) if !ok { return value.NewIntValue(0), false } if t, err := dateparse.ParseAny(dateStr); err == nil { //u.Infof("v=%v %v unix=%v", item, item.Rv(), t.Unix()) return value.NewIntValue(int64(t.Unix())), true } return value.NewIntValue(0), false }
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") }
// hour of day [0-23] func HourOfDay(ctx expr.EvalContext, items ...value.Value) (value.IntValue, bool) { if len(items) == 0 { if !ctx.Ts().IsZero() { return value.NewIntValue(int64(ctx.Ts().Hour())), true } } else if len(items) == 1 { dateStr, ok := value.ToString(items[0].Rv()) if !ok { return value.NewIntValue(0), false } //u.Infof("v=%v %v ", v, items[0].Rv()) if t, err := dateparse.ParseAny(dateStr); err == nil { return value.NewIntValue(int64(t.Hour())), true } } return value.NewIntValue(0), false }
func Yy(ctx expr.EvalContext, item value.Value) (value.IntValue, bool) { //u.Info("yy: %T", item) val, ok := value.ToString(item.Rv()) if !ok || val == "" { return value.NewIntValue(0), false } //u.Infof("v=%v %v ", val, item.Rv()) if t, err := dateparse.ParseAny(val); err == nil { yy := t.Year() if yy >= 2000 { yy = yy - 2000 } else if yy >= 1900 { yy = yy - 1900 } //u.Infof("Yy = %v yy = %v", item, yy) return value.NewIntValue(int64(yy)), true } return value.NewIntValue(0), false }
// creates a new Value with a nil group and given value. // TODO: convert this to an interface method on nodes called Value() func numberNodeToValue(t *expr.NumberNode) (v value.Value) { //u.Debugf("nodeToValue() isFloat?%v", t.IsFloat) if t.IsInt { v = value.NewIntValue(t.Int64) } else if t.IsFloat { v = value.NewNumberValue(value.ToFloat64(reflect.ValueOf(t.Text))) } else { u.Errorf("Could not find type? %v", t.Type()) } //u.Debugf("return nodeToValue() %v %T arg:%T", v, v, t) return v }
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) } } }
// creates a new Value with a nil group and given value. // TODO: convert this to an interface method on nodes called Value() func numberNodeToValue(t *expr.NumberNode) (value.Value, bool) { //u.Debugf("nodeToValue() isFloat?%v", t.IsFloat) var v value.Value if t.IsInt { v = value.NewIntValue(t.Int64) } else if t.IsFloat { fv, ok := value.ToFloat64(reflect.ValueOf(t.Text)) if !ok { u.Warnf("Could not perform numeric conversion for %q", t.Text) return value.NilValueVal, false } v = value.NewNumberValue(fv) } else { u.Warnf("Could not find numeric conversion for %v", t.Type()) return value.NilValueVal, false } //u.Debugf("return nodeToValue() %v %T arg:%T", v, v, t) return v, true }
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() } } }
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 }
func ToInt(ctx expr.EvalContext, item value.Value) (value.IntValue, bool) { iv, _ := value.ToInt64(reflect.ValueOf(item.Value())) return value.NewIntValue(iv), true //return IntValue(2) }
u.SetupLogging("debug") u.SetColorOutput() } expr.FuncAdd("eq", Eq) expr.FuncAdd("toint", ToInt) expr.FuncAdd("yy", Yy) } var ( // This is the message context which will be added to all tests below // and be available to the VM runtime for evaluation by using // key's such as "int5" or "user_id" msgContext = datasource.NewContextSimpleData(map[string]value.Value{ "int5": value.NewIntValue(5), "str5": value.NewStringValue("5"), "bvalt": value.NewBoolValue(true), "bvalf": value.NewBoolValue(false), "user_id": value.NewStringValue("abc"), }) // list of tests vmTests = []vmTest{ // Between: Tri Node Tests vmt("tri between ints", `10 BETWEEN 1 AND 50`, true, noError), vmt("tri between ints false", `10 BETWEEN 20 AND 50`, false, noError), vmtall("tri between ints false", `10 BETWEEN 20 AND true`, nil, parseOk, evalError), // In: Multi Arg Tests vmtall("multi-arg: In (x,y,z) ", `10 IN ("a","b",10, 4.5)`, true, parseOk, evalError),
// len: length of array types // // len([1,2,3]) => 3, true // len(not_a_field) => -- NilInt, false // func LengthFunc(ctx expr.EvalContext, val value.Value) (value.IntValue, bool) { switch node := val.(type) { case value.StringValue: return value.NewIntValue(int64(len(node.Val()))), true case value.BoolValue: return value.NewIntValue(0), true case value.NumberValue: return value.NewIntValue(0), true case value.IntValue: return value.NewIntValue(0), true case value.TimeValue: return value.NewIntValue(0), true case value.StringsValue: return value.NewIntValue(int64(node.Len())), true case value.SliceValue: return value.NewIntValue(int64(node.Len())), true case value.ByteSliceValue: return value.NewIntValue(int64(node.Len())), true case value.MapIntValue: return value.NewIntValue(int64(len(node.Val()))), true case value.MapNumberValue: return value.NewIntValue(int64(len(node.Val()))), true case value.MapStringValue: return value.NewIntValue(int64(len(node.Val()))), true case value.MapValue: return value.NewIntValue(int64(len(node.Val()))), true case nil, value.NilValue: return value.NewIntNil(), false } return value.NewIntNil(), false }
// Count: This should be renamed Increment // and in general is a horrible, horrible function that needs to be replaced // with occurences of value, ignores the value and ensures it is non null // // count(anyvalue) => 1, true // count(not_number) => -- 0, false // func CountFunc(ctx expr.EvalContext, val value.Value) (value.IntValue, bool) { if val.Err() || val.Nil() { return value.NewIntValue(0), false } return value.NewIntValue(1), true }
regTime, _ = dateparse.ParseAny(regDate) readContext = datasource.NewContextUrlValuesTs(url.Values{ "event": {"hello"}, "reg_date": {"10/13/2014"}, "price": {"$55"}, "email": {"*****@*****.**"}, "url": {"http://www.site.com/membership/all.html"}, "score_amount": {"22"}, "tag_name": {"bob"}, }, ts) float3pt1 = float64(3.1) ) var builtinTestsx = []testBuiltins{ {`cast(reg_date as time)`, value.NewTimeValue(regTime)}, {`CHAR_LENGTH(CAST("abc" AS CHAR))`, value.NewIntValue(3)}, } var builtinTests = []testBuiltins{ {`eq(5,5)`, value.BoolValueTrue}, {`eq("hello", event)`, value.BoolValueTrue}, {`eq(5,6)`, value.BoolValueFalse}, {`eq(5.5,6)`, value.BoolValueFalse}, {`eq(true,eq(5,5))`, value.BoolValueTrue}, {`eq(true,false)`, value.BoolValueFalse}, {`eq(not_a_field,5)`, value.BoolValueFalse}, {`eq(eq(not_a_field,5),false)`, value.BoolValueTrue}, {`ne(5,5)`, value.BoolValueFalse}, {`ne("hello", event)`, value.BoolValueFalse}, {`ne("hello", fakeevent)`, value.BoolValueTrue},
{`path("c://Windows/really")`, value.NewStringValue("//windows/really")}, {`path("/home/aaron/vm")`, value.NewStringValue("/home/aaron/vm")}, {`qs("https://www.Google.com/search?q=golang","q")`, value.NewStringValue("golang")}, {`qs("www.Google.com/?q=golang","q")`, value.NewStringValue("golang")}, {`urlminusqs("http://www.Google.com/search?q1=golang&q2=github","q1")`, value.NewStringValue("http://www.Google.com/search?q2=github")}, {`urlminusqs("http://www.Google.com/search?q1=golang&q2=github","q3")`, value.NewStringValue("http://www.Google.com/search?q1=golang&q2=github")}, {`urlminusqs("http://www.Google.com/search?q1=golang","q1")`, value.NewStringValue("http://www.Google.com/search")}, {`urlmain("http://www.Google.com/search?q1=golang&q2=github")`, value.NewStringValue("www.Google.com/search")}, // ts2 = time.Date(2014, 4, 7, 0, 0, 0, 00, time.UTC) // Eu style {`todate("02/01/2006","07/04/2014")`, value.NewTimeValue(ts2)}, {`toint("5")`, value.NewIntValue(5)}, {`toint("hello")`, value.ErrValue}, {`toint("$ 5.22")`, value.NewIntValue(5)}, {`toint("5.56")`, value.NewIntValue(5)}, {`toint("$5.56")`, value.NewIntValue(5)}, {`toint("5,555.00")`, value.NewIntValue(5555)}, {`toint("€ 5,555.00")`, value.NewIntValue(5555)}, {`tonumber("5")`, value.NewNumberValue(float64(5))}, {`tonumber("hello")`, value.ErrValue}, {`tonumber("$ 5.22")`, value.NewNumberValue(float64(5.22))}, {`tonumber("5.56")`, value.NewNumberValue(float64(5.56))}, {`tonumber("$5.56")`, value.NewNumberValue(float64(5.56))}, {`tonumber("5,555.00")`, value.NewNumberValue(float64(5555.00))}, {`tonumber("€ 5,555.00")`, value.NewNumberValue(float64(5555.00))},
func ToInt(e *State, item value.Value) (value.IntValue, bool) { iv, _ := value.ToInt64(reflect.ValueOf(item.Value())) return value.NewIntValue(iv), true //return IntValue(2) }
func operateIntVals(op lex.Token, a, b int64) (value.Value, error) { //u.Infof("a op b: %v %v %v", a, op.V, b) switch op.T { case lex.TokenPlus: // + //r = a + b return value.NewIntValue(a + b), nil case lex.TokenStar, lex.TokenMultiply: // * //r = a * b return value.NewIntValue(a * b), nil case lex.TokenMinus: // - //r = a - b return value.NewIntValue(a - b), nil case lex.TokenDivide: // / //r = a / b //u.Debugf("divide: %v / %v = %v", a, b, a/b) return value.NewIntValue(a / b), nil case lex.TokenModulus: // % //r = a / b //u.Debugf("modulus: %v / %v = %v", a, b, a/b) return value.NewIntValue(a % b), nil // Below here are Boolean Returns case lex.TokenEqualEqual, lex.TokenEqual: // ==, = if a == b { return value.BoolValueTrue, nil } else { return value.BoolValueFalse, nil } case lex.TokenGT: // > if a > b { return value.BoolValueTrue, nil } else { return value.BoolValueFalse, nil } case lex.TokenNE: // != or <> if a != b { return value.BoolValueTrue, nil } else { return value.BoolValueFalse, nil } case lex.TokenLT: // < if a < b { return value.BoolValueTrue, nil } else { return value.BoolValueFalse, nil } case lex.TokenGE: // >= if a >= b { return value.BoolValueTrue, nil } else { return value.BoolValueFalse, nil } case lex.TokenLE: // <= if a <= b { return value.BoolValueTrue, nil } else { return value.BoolValueFalse, nil } case lex.TokenLogicOr, lex.TokenOr: // || if a != 0 || b != 0 { return value.BoolValueTrue, nil } else { return value.BoolValueFalse, nil } case lex.TokenLogicAnd: // && if a != 0 && b != 0 { return value.BoolValueTrue, nil } else { return value.BoolValueFalse, nil } } return nil, fmt.Errorf("expr: unknown operator %s", op) }
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") }
func operateInts(op lex.Token, av, bv value.IntValue) value.Value { //if math.IsNaN(a) || math.IsNaN(b) { // return math.NaN() //} a, b := av.Val(), bv.Val() //u.Infof("a op b: %v %v %v", a, op.V, b) switch op.T { case lex.TokenPlus: // + //r = a + b return value.NewIntValue(a + b) case lex.TokenStar, lex.TokenMultiply: // * //r = a * b return value.NewIntValue(a * b) case lex.TokenMinus: // - //r = a - b return value.NewIntValue(a - b) case lex.TokenDivide: // / //r = a / b //u.Debugf("divide: %v / %v = %v", a, b, a/b) return value.NewIntValue(a / b) case lex.TokenModulus: // % //r = a / b //u.Debugf("modulus: %v / %v = %v", a, b, a/b) return value.NewIntValue(a % b) // Below here are Boolean Returns case lex.TokenEqualEqual: // == if a == b { return value.BoolValueTrue } else { return value.BoolValueFalse } case lex.TokenGT: // > if a > b { return value.BoolValueTrue } else { return value.BoolValueFalse } case lex.TokenNE: // != or <> if a != b { return value.BoolValueTrue } else { return value.BoolValueFalse } case lex.TokenLT: // < if a < b { return value.BoolValueTrue } else { return value.BoolValueFalse } case lex.TokenGE: // >= if a >= b { return value.BoolValueTrue } else { return value.BoolValueFalse } case lex.TokenLE: // <= if a <= b { return value.BoolValueTrue } else { return value.BoolValueFalse } case lex.TokenLogicOr, lex.TokenOr: // || if a != 0 || b != 0 { return value.BoolValueTrue } else { return value.BoolValueFalse } case lex.TokenLogicAnd: // && if a != 0 && b != 0 { return value.BoolValueTrue } else { return value.BoolValueFalse } } panic(fmt.Errorf("expr: unknown operator %s", op)) }