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)
	}
}
Exemple #3
0
// 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())
		}
	}
}