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 TestStructWrapper(t *testing.T) { t1, _ := dateparse.ParseAny("12/18/2015") tr := true user := &User{ Name: "Yoda", Created: t1, Updated: &t1, Authenticated: true, HasSession: &tr, Roles: []string{"admin", "api"}, BankAmount: 55.5, } readers := []expr.ContextReader{ datasource.NewContextWrapper(user), datasource.NewContextSimpleNative(map[string]interface{}{ "str1": "str1", "int1": 1, "t1": t1, "Name": "notyoda", }), } nc := datasource.NewNestedContextReader(readers, time.Now()) expected := value.NewMapValue(map[string]interface{}{ "str1": "str1", "int1": 1, "Name": "Yoda", "Authenticated": true, "bankamount": 55.5, "FullName": "Yoda, Jedi", "Roles": []string{"admin", "api"}, }) for k, v := range expected.Val() { //u.Infof("k:%v v:%#v", k, v) checkval(t, nc, k, v) } }
// Create handler function for evaluation (ie, field selection from tuples) func (m *Projection) projectionEvaluator(isFinal bool) MessageHandler { out := m.MessageOut() columns := m.p.Stmt.Columns colIndex := m.p.Stmt.ColIndexes() limit := m.p.Stmt.Limit if limit == 0 { limit = math.MaxInt32 } colCt := len(columns) // If we have a projection, use that as col count if m.p.Proj != nil { colCt = len(m.p.Proj.Columns) } // if m.p.Proj == nil { // u.Warnf("What, requires Projection? %#v", m.p) // } //u.Debugf("limit: %d colindex: %#v", limit, colIndex) //u.Debugf("plan projection columns: %#v", m.p.Proj.Columns) //u.Debugf("columns: %#v", columns) rowCt := 0 return func(ctx *plan.Context, msg schema.Message) bool { select { case <-m.SigChan(): u.Debugf("%p closed, returning", m) return false default: } //u.Infof("got projection message: %T %#v", msg, msg.Body()) var outMsg schema.Message switch mt := msg.(type) { case *datasource.SqlDriverMessageMap: // use our custom write context for example purposes row := make([]driver.Value, colCt) rdr := datasource.NewNestedContextReader([]expr.ContextReader{ mt, ctx.Session, }, mt.Ts()) //u.Debugf("about to project: %#v", mt) colCt := 0 for i, col := range columns { //u.Debugf("col: idx:%v sidx: %v pidx:%v key:%v %s", col.Index, col.SourceIndex, col.ParentIndex, col.Key(), col.Expr) if isFinal && col.ParentIndex < 0 { continue } if col.Guard != nil { ifColValue, ok := vm.Eval(rdr, col.Guard) if !ok { u.Errorf("Could not evaluate if: %v", col.Guard.String()) //return fmt.Errorf("Could not evaluate if clause: %v", col.Guard.String()) } //u.Debugf("if eval val: %T:%v", ifColValue, ifColValue) switch ifColVal := ifColValue.(type) { case value.BoolValue: if ifColVal.Val() == false { //u.Debugf("Filtering out col") continue } } } if col.Star { starRow := mt.Values() //u.Infof("star row: %#v", starRow) if len(columns) > 1 { // select *, myvar, 1 newRow := make([]driver.Value, len(starRow)+len(colIndex)-1) for curi := 0; curi < i; curi++ { newRow[curi] = row[curi] } row = newRow for _, v := range starRow { colCt += 1 //writeContext.Put(&expr.Column{As: k}, nil, value.NewValue(v)) row[i+colCt] = v } } else { colCt-- for _, v := range starRow { colCt += 1 //writeContext.Put(&expr.Column{As: k}, nil, value.NewValue(v)) //u.Infof("i:%d colct: %v v:%v", i, colCt, v) row[i+colCt] = v } } } else if col.Expr == nil { u.Warnf("wat? nil col expr? %#v", col) } else { v, ok := vm.Eval(rdr, col.Expr) if !ok { u.Warnf("failed eval key=%v val=%#v expr:%s mt:%#v", col.Key(), v, col.Expr, mt) // for k, v := range ctx.Session.Row() { // u.Infof("%p session? %s: %v", ctx.Session, k, v.Value()) // } } else if v == nil { //u.Debugf("%#v", col) //u.Debugf("evaled nil? key=%v val=%v expr:%s", col.Key(), v, col.Expr.String()) //writeContext.Put(col, mt, v) //u.Infof("mt: %T mt %#v", mt, mt) row[i+colCt] = nil //v.Value() } else { //u.Debugf("evaled: key=%v val=%v", col.Key(), v.Value()) //writeContext.Put(col, mt, v) row[i+colCt] = v.Value() } } } //u.Infof("row: %#v", row) //u.Infof("row cols: %v", colIndex) outMsg = datasource.NewSqlDriverMessageMap(0, row, colIndex) case expr.ContextReader: //u.Warnf("nice, got context reader? %T", mt) row := make([]driver.Value, len(columns)) //u.Debugf("about to project: %#v", mt) colCt := 0 for i, col := range columns { //u.Debugf("col: idx:%v sidx: %v pidx:%v key:%v %s", col.Index, col.SourceIndex, col.ParentIndex, col.Key(), col.Expr) if isFinal && col.ParentIndex < 0 { continue } if col.Guard != nil { ifColValue, ok := vm.Eval(mt, col.Guard) if !ok { u.Errorf("Could not evaluate if: %v", col.Guard.String()) //return fmt.Errorf("Could not evaluate if clause: %v", col.Guard.String()) } //u.Debugf("if eval val: %T:%v", ifColValue, ifColValue) switch ifColVal := ifColValue.(type) { case value.BoolValue: if ifColVal.Val() == false { //u.Debugf("Filtering out col") continue } } } if col.Star { starRow := mt.Row() newRow := make([]driver.Value, len(starRow)+len(colIndex)) for curi := 0; curi < i; curi++ { newRow[curi] = row[curi] } row = newRow for _, v := range starRow { colCt += 1 //writeContext.Put(&expr.Column{As: k}, nil, value.NewValue(v)) row[i+colCt] = v } } else if col.Expr == nil { u.Warnf("wat? nil col expr? %#v", col) } else { v, ok := vm.Eval(mt, col.Expr) if !ok { //u.Warnf("failed eval key=%v val=%#v expr:%s mt:%#v", col.Key(), v, col.Expr, mt.Row()) } else if v == nil { //u.Debugf("%#v", col) //u.Debugf("evaled nil? key=%v val=%v expr:%s", col.Key(), v, col.Expr.String()) //writeContext.Put(col, mt, v) //u.Infof("mt: %T mt %#v", mt, mt) row[i+colCt] = nil //v.Value() } else { //u.Debugf("evaled: key=%v val=%v", col.Key(), v.Value()) //writeContext.Put(col, mt, v) row[i+colCt] = v.Value() } } } //u.Infof("row: %#v cols:%#v", row, colIndex) //u.Infof("row cols: %v", colIndex) outMsg = datasource.NewSqlDriverMessageMap(0, row, colIndex) default: u.Errorf("could not project msg: %T", msg) } if rowCt >= limit { //u.Debugf("%p Projection reaching Limit!!! rowct:%v limit:%v", m, rowCt, limit) out <- nil // Sending nil message is a message to downstream to shutdown m.Quit() // should close rest of dag as well return false } rowCt++ //u.Debugf("row:%d completed projection for: %p %#v", rowCt, out, outMsg) select { case out <- outMsg: return true case <-m.SigChan(): return false } } }
func TestFilterQlVm(t *testing.T) { t.Parallel() t1, _ := dateparse.ParseAny("12/18/2015") //u.Infof("t1 %v", t1) nminus1 := time.Now().Add(time.Hour * -1) tr := true user := &User{ Name: "Yoda", Created: t1, Updated: &nminus1, Authenticated: true, HasSession: &tr, Address: Address{"Detroit", 55}, Roles: []string{"admin", "api"}, BankAmount: 55.5, } readers := []expr.ContextReader{ datasource.NewContextWrapper(user), datasource.NewContextSimpleNative(map[string]interface{}{ "city": "Peoria, IL", "zip": 5, }), } nc := datasource.NewNestedContextReader(readers, time.Now()) hits := []string{ `FILTER name == "Yoda"`, // upper case sensitive name `FILTER name != "yoda"`, // we should be case-sensitive by default `FILTER name = "Yoda"`, // is equivalent to == `FILTER "Yoda" == name`, // reverse order of identity/value `FILTER name != "Anakin"`, // negation on missing fields == true `FILTER first_name != "Anakin"`, // key doesn't exist `FILTER tolower(name) == "yoda"`, // use functions in evaluation `FILTER FullName == "Yoda, Jedi"`, // use functions on structs in evaluation `FILTER Address.City == "Detroit"`, // traverse struct with path.field `FILTER name LIKE "*da"`, // LIKE `FILTER name NOT LIKE "*kin"`, // LIKE Negation `FILTER name CONTAINS "od"`, // Contains `FILTER name NOT CONTAINS "kin"`, // Contains `FILTER roles INTERSECTS ("user", "api")`, // Intersects `FILTER roles NOT INTERSECTS ("user", "guest")`, // Intersects `FILTER Created < "now-1d"`, // Date Math `FILTER Updated > "now-2h"`, // Date Math `FILTER *`, // match all `FILTER OR ( EXISTS name, -- inline comments EXISTS not_a_key, -- more inline comments )`, // show that line-breaks serve as expression separators `FILTER OR ( EXISTS name EXISTS not_a_key -- even if they have inline comments )`, //`FILTER a == "Yoda" AND b == "Peoria, IL" AND c == 5`, `FILTER AND (name == "Yoda", city == "Peoria, IL", zip == 5, BankAmount > 50)`, `FILTER AND (zip == 5, "Yoda" == name, OR ( city IN ( "Portland, OR", "New York, NY", "Peoria, IL" ) ) )`, `FILTER OR ( EXISTS q, AND ( zip > 0, OR ( zip > 10000, zip < 100 ) ), NOT ( name == "Yoda" ) )`, } for _, q := range hits { fs, err := rel.ParseFilterQL(q) assert.Equal(t, nil, err) match, err := NewFilterVm(nil).Matches(nc, fs) assert.Equalf(t, nil, err, "error matching on query %q: %v", q, err) assert.T(t, match, q) } misses := []string{ `FILTER name == "yoda"`, // casing "FILTER OR (false, false, AND (true, false))", `FILTER AND (name == "Yoda", city == "xxx", zip == 5)`, } for _, q := range misses { fs, err := rel.ParseFilterQL(q) assert.Equal(t, nil, err) match, err := NewFilterVm(nil).Matches(nc, fs) assert.Equal(t, nil, err) assert.T(t, !match) } // Filter Select Statements filterSelects := []fsel{ fsel{`select name, zip FROM mycontext FILTER name == "Yoda"`, map[string]interface{}{"name": "Yoda", "zip": 5}}, } for _, test := range filterSelects { //u.Debugf("about to parse: %v", test.qlText) sel, err := rel.ParseFilterSelect(test.query) assert.T(t, err == nil, "expected no error but got ", err, " for ", test.query) writeContext := datasource.NewContextSimple() _, err = EvalFilerSelect(sel, nil, writeContext, nc) assert.T(t, err == nil, "expected no error but got ", err, " for ", test.query) for key, val := range test.expect { v := value.NewValue(val) v2, ok := writeContext.Get(key) assert.Tf(t, ok, "Get(%q)=%v but got: %#v", key, val, writeContext.Row()) assert.Equalf(t, v2.Value(), v.Value(), "?? %s %v!=%v %T %T", key, v.Value(), v2.Value(), v.Value(), v2.Value()) } } }