func TestDataAccessTripleToRowObjectBindingsDroping(t *testing.T) { n, p, _ := testNodeTemporalPredicateLiteral(t) ts, err := p.TimeAnchor() if err != nil { t.Fatal(err) } testTable := []struct { t string cls *semantic.GraphClause bc *table.Cell ac *table.Cell tc *table.Cell ic *table.Cell tsc *table.Cell atc *table.Cell }{ { t: n.String() + "\t" + p.String() + "\t" + n.String(), cls: &semantic.GraphClause{ OBinding: "?o", OAlias: "?alias", OTypeAlias: "?o", OIDAlias: "?id", }, bc: &table.Cell{N: n}, ac: &table.Cell{N: n}, tc: &table.Cell{S: table.CellString(n.Type().String())}, ic: &table.Cell{S: table.CellString(n.ID().String())}, }, { t: n.String() + "\t" + p.String() + "\t" + p.String(), cls: &semantic.GraphClause{ OBinding: "?o", OAlias: "?alias", OIDAlias: "?id", OAnchorBinding: "?ts", OAnchorAlias: "?o", }, bc: &table.Cell{P: p}, ac: &table.Cell{P: p}, ic: &table.Cell{S: table.CellString(string(p.ID()))}, tsc: &table.Cell{T: ts}, atc: &table.Cell{T: ts}, }, } for _, entry := range testTable { tpl, err := triple.Parse(entry.t, literal.DefaultBuilder()) if err != nil { t.Errorf("triple.Parse failed to parse valid triple %q with error %v", entry.t, err) } r, err := tripleToRow(tpl, entry.cls) if err != nil { t.Errorf("tripleToRow for triple %q and clasuse %v, failed with error %v", tpl, entry.cls, err) } if r != nil { t.Errorf("tripleToRow for triple %q and clasuse %v, failed to drop triple and returned %v", tpl, entry.cls, r) } } }
func TestDataAccessTripleToRowSubjectBindings(t *testing.T) { n, p, _ := testNodePredicateLiteral(t) testTable := []struct { t string cls *semantic.GraphClause sc *table.Cell ac *table.Cell tc *table.Cell ic *table.Cell }{ { t: n.String() + "\t" + p.String() + "\t" + n.String(), cls: &semantic.GraphClause{ SBinding: "?s", SAlias: "?alias", STypeAlias: "?type", SIDAlias: "?id", }, sc: &table.Cell{N: n}, ac: &table.Cell{N: n}, tc: &table.Cell{S: table.CellString(n.Type().String())}, ic: &table.Cell{S: table.CellString(n.ID().String())}, }, } for _, entry := range testTable { tpl, err := triple.Parse(entry.t, literal.DefaultBuilder()) if err != nil { t.Errorf("triple.Parse failed to parse valid triple %q with error %v", entry.t, err) } r, err := tripleToRow(tpl, entry.cls) if err != nil { t.Errorf("tripleToRow for triple %q and clasuse %v, failed with error %v", tpl, entry.cls, err) } if got, want := r["?s"], entry.sc; !reflect.DeepEqual(got, want) { t.Errorf("tripleToRow failed to return right value for binding \"?s\"; got %q, want %q", got, want) } if got, want := r["?alias"], entry.ac; !reflect.DeepEqual(got, want) { t.Errorf("tripleToRow failed to return right value for binding alias \"?alias\"; got %q, want %q", got, want) } if got, want := r["?type"], entry.tc; !reflect.DeepEqual(got, want) { t.Errorf("tripleToRow failed to return right value for binding \"?type\"; got %q, want %q", got, want) } if got, want := r["?id"], entry.ic; !reflect.DeepEqual(got, want) { t.Errorf("tripleToRow failed to return right value for binding \"?ud\"; got %q, want %q", got, want) } } }
func TestDataAccessTripleToRowPredicateBindings(t *testing.T) { n, p, _ := testNodeTemporalPredicateLiteral(t) ts, err := p.TimeAnchor() if err != nil { t.Fatal(err) } testTable := []struct { t string cls *semantic.GraphClause bc *table.Cell ac *table.Cell ic *table.Cell tc *table.Cell atc *table.Cell }{ { t: n.String() + "\t" + p.String() + "\t" + n.String(), cls: &semantic.GraphClause{ PBinding: "?p", PAlias: "?alias", PIDAlias: "?id", PAnchorBinding: "?ts", PAnchorAlias: "?tsa", }, bc: &table.Cell{P: p}, ac: &table.Cell{P: p}, ic: &table.Cell{S: table.CellString(string(p.ID()))}, tc: &table.Cell{T: ts}, atc: &table.Cell{T: ts}, }, } for _, entry := range testTable { tpl, err := triple.Parse(entry.t, literal.DefaultBuilder()) if err != nil { t.Errorf("triple.Parse failed to parse valid triple %q with error %v", entry.t, err) } r, err := tripleToRow(tpl, entry.cls) if err != nil { t.Errorf("tripleToRow for triple %q and clasuse %v, failed with error %v", tpl, entry.cls, err) } if got, want := r["?p"], entry.bc; !reflect.DeepEqual(got, want) { t.Errorf("tripleToRow failed to return right value for binding \"?p\"; got %q, want %q", got, want) } if got, want := r["?alias"], entry.ac; !reflect.DeepEqual(got, want) { t.Errorf("tripleToRow failed to return right value for binding alias \"?alias\"; got %q, want %q", got, want) } if got, want := r["?id"], entry.ic; !reflect.DeepEqual(got, want) { t.Errorf("tripleToRow failed to return right value for binding \"?id\"; got %q, want %q", got, want) } if got, want := r["?ts"], entry.tc; !reflect.DeepEqual(got, want) { t.Errorf("tripleToRow failed to return right value for binding \"?ts\"; got %q, want %q", got, want) } if got, want := r["?tsa"], entry.tc; !reflect.DeepEqual(got, want) { t.Errorf("tripleToRow failed to return right value for binding \"?tsa\"; got %q, want %q", got, want) } } }
// inferCell builds a Cell out of the provided string. func inferCell(s string) *table.Cell { if n, err := node.Parse(s); err == nil { return &table.Cell{N: n} } if p, err := predicate.Parse(s); err == nil { return &table.Cell{P: p} } if l, err := literal.DefaultBuilder().Parse(s); err == nil { return &table.Cell{L: l} } t, err := time.Parse(time.RFC3339Nano, s) if err == nil { return &table.Cell{T: &t} } return &table.Cell{S: table.CellString(s)} }
func TestEvaluationNode(t *testing.T) { testTable := []struct { eval Evaluator r table.Row want bool err bool }{ { eval: &evaluationNode{EQ, "?foo", "?wrong_binding"}, r: table.Row{ "?foo": &table.Cell{S: table.CellString("foo")}, "?bar": &table.Cell{S: table.CellString("foo")}, }, want: false, err: true, }, { eval: &evaluationNode{EQ, "?foo", "?bar"}, r: table.Row{ "?foo": &table.Cell{S: table.CellString("foo")}, "?bar": &table.Cell{S: table.CellString("bar")}, }, want: false, err: false, }, { eval: &evaluationNode{EQ, "", "?bar"}, r: table.Row{ "?foo": &table.Cell{S: table.CellString("foo")}, "?bar": &table.Cell{S: table.CellString("bar")}, }, want: false, err: true, }, { eval: &evaluationNode{EQ, "?foo", ""}, r: table.Row{ "?foo": &table.Cell{S: table.CellString("foo")}, "?bar": &table.Cell{S: table.CellString("bar")}, }, want: false, err: true, }, { eval: &evaluationNode{EQ, "?foo", "?bar"}, r: table.Row{ "?foo": &table.Cell{S: table.CellString("foo")}, "?bar": &table.Cell{S: table.CellString("foo")}, }, want: true, err: false, }, { eval: &evaluationNode{LT, "?foo", "?bar"}, r: table.Row{ "?foo": &table.Cell{S: table.CellString("foo")}, "?bar": &table.Cell{S: table.CellString("bar")}, }, want: false, err: false, }, { eval: &evaluationNode{GT, "?foo", "?bar"}, r: table.Row{ "?foo": &table.Cell{S: table.CellString("foo")}, "?bar": &table.Cell{S: table.CellString("bar")}, }, want: true, err: false, }, } for _, entry := range testTable { got, err := entry.eval.Evaluate(entry.r) if !entry.err && err != nil { t.Errorf("failed to evaluate op %q for %v on row %v with error %v", entry.eval.(*evaluationNode).op, entry.eval, entry.r, err) } if want := entry.want; got != want { t.Errorf("failed to evaluate op %q for %v on row %v; got %v, want %v", entry.eval.(*evaluationNode).op, entry.eval, entry.r, got, want) } } }
func TestNewEvaluator(t *testing.T) { testTable := []struct { id string in []ConsumedElement r table.Row err bool want bool }{ { id: "?foo = ?bar", in: []ConsumedElement{ NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?foo", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemEQ, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?bar", }), }, r: table.Row{ "?foo": &table.Cell{S: table.CellString("VALUE")}, "?bar": &table.Cell{S: table.CellString("VALUE")}, }, err: false, want: true, }, { id: "?foo < ?bar", in: []ConsumedElement{ NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?foo", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemLT, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?bar", }), }, r: table.Row{ "?foo": &table.Cell{S: table.CellString("foo")}, "?bar": &table.Cell{S: table.CellString("bar")}, }, err: false, want: false, }, { id: "?foo > ?bar", in: []ConsumedElement{ NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?foo", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemGT, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?bar", }), }, r: table.Row{ "?foo": &table.Cell{S: table.CellString("foo")}, "?bar": &table.Cell{S: table.CellString("bar")}, }, err: false, want: true, }, { id: "not(?foo = ?bar)", in: []ConsumedElement{ NewConsumedToken(&lexer.Token{ Type: lexer.ItemNot, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemLPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?foo", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemEQ, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?bar", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemRPar, }), }, r: table.Row{ "?foo": &table.Cell{S: table.CellString("VALUE")}, "?bar": &table.Cell{S: table.CellString("VALUE")}, }, err: false, want: false, }, { id: "(?foo < ?bar) or (?foo > ?bar)", in: []ConsumedElement{ NewConsumedToken(&lexer.Token{ Type: lexer.ItemLPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?foo", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemLT, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?bar", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemRPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemOr, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemLPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?foo", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemGT, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?bar", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemRPar, }), }, r: table.Row{ "?foo": &table.Cell{S: table.CellString("foo")}, "?bar": &table.Cell{S: table.CellString("bar")}, }, err: false, want: true, }, { id: "(?foo < ?bar) and (?foo > ?bar)", in: []ConsumedElement{ NewConsumedToken(&lexer.Token{ Type: lexer.ItemLPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?foo", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemLT, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?bar", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemRPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemAnd, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemLPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?foo", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemGT, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?bar", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemRPar, }), }, r: table.Row{ "?foo": &table.Cell{S: table.CellString("foo")}, "?bar": &table.Cell{S: table.CellString("bar")}, }, err: false, want: false, }, } for _, entry := range testTable { eval, err := NewEvaluator(entry.in) if !entry.err && err != nil { t.Fatalf("test %q should have never failed to process %v with error %v", entry.id, entry.in, err) } got, err := eval.Evaluate(entry.r) if err != nil { t.Errorf("test %q the created evaluator failed to evaluate row %v with error %v", entry.id, entry.r, err) } if want := entry.want; got != want { t.Errorf("test %q failed to evaluate the proper value; got %v, want %v", entry.id, got, want) } } }
func TestHavingExpressionBuilder(t *testing.T) { f := havingExpressionBuilder() testTable := []struct { id string s *Statement r table.Row want bool }{ { id: "empty statement", s: &Statement{}, want: true, }, { id: "(?foo < ?bar) or (?foo > ?bar)", s: &Statement{ havingExpression: []ConsumedElement{ NewConsumedToken(&lexer.Token{ Type: lexer.ItemLPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?foo", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemLT, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?bar", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemRPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemOr, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemLPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?foo", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemGT, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?bar", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemRPar, }), }, }, r: table.Row{ "?foo": &table.Cell{S: table.CellString("foo")}, "?bar": &table.Cell{S: table.CellString("bar")}, }, want: true, }, { id: "not((?foo < ?bar) or (?foo > ?bar))", s: &Statement{ havingExpression: []ConsumedElement{ NewConsumedToken(&lexer.Token{ Type: lexer.ItemNot, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemLPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemLPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?foo", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemLT, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?bar", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemRPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemOr, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemLPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?foo", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemGT, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemBinding, Text: "?bar", }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemRPar, }), NewConsumedToken(&lexer.Token{ Type: lexer.ItemRPar, }), }, }, r: table.Row{ "?foo": &table.Cell{S: table.CellString("foo")}, "?bar": &table.Cell{S: table.CellString("bar")}, }, want: false, }, } for _, entry := range testTable { if _, err := f(entry.s, Symbol("FOO")); err != nil { t.Errorf("semantic.havingExpressionBuilder faile to build statement %#v for case %q with error %v", entry.s, entry.id, err) } got, err := entry.s.havingExpressionEvaluator.Evaluate(entry.r) if err != nil { t.Errorf("expression evaluator should have not fail with errorf %v for case %q", err, entry.id) } if want := entry.want; got != want { t.Errorf("expression evaluator returned the wrong value for case %q; got %v, want %v", entry.id, got, want) } } }
// tripleToRow converts a triple into a row using the binndings specidfied // on the graph clause. func tripleToRow(t *triple.Triple, cls *semantic.GraphClause) (table.Row, error) { r, s, p, o := make(table.Row), t.Subject(), t.Predicate(), t.Object() // Enforce binding validity inside te clause. bnd := make(map[string]*table.Cell) validBinding := func(k string, v *table.Cell) bool { c, ok := bnd[k] bnd[k] = v if !ok { return true } if reflect.DeepEqual(c, v) { return true } return false } // Subject related bindings. if cls.SBinding != "" { c := &table.Cell{N: s} r[cls.SBinding] = c if !validBinding(cls.SBinding, c) { return nil, nil } } if cls.SAlias != "" { c := &table.Cell{N: s} r[cls.SAlias] = c if !validBinding(cls.SAlias, c) { return nil, nil } } if cls.STypeAlias != "" { c := &table.Cell{S: table.CellString(s.Type().String())} r[cls.STypeAlias] = c if !validBinding(cls.STypeAlias, c) { return nil, nil } } if cls.SIDAlias != "" { c := &table.Cell{S: table.CellString(s.ID().String())} r[cls.SIDAlias] = c if !validBinding(cls.SIDAlias, c) { return nil, nil } } // Predicate related bindings. if cls.PBinding != "" { c := &table.Cell{P: p} r[cls.PBinding] = c if !validBinding(cls.PBinding, c) { return nil, nil } } if cls.PAlias != "" { c := &table.Cell{P: p} r[cls.PAlias] = c if !validBinding(cls.PAlias, c) { return nil, nil } } if cls.PIDAlias != "" { c := &table.Cell{S: table.CellString(string(p.ID()))} r[cls.PIDAlias] = c if !validBinding(cls.PIDAlias, c) { return nil, nil } } if cls.PAnchorBinding != "" { if p.Type() != predicate.Temporal { return nil, fmt.Errorf("cannot retrieve the time anchor value for non temporal predicate %q in binding %q", p, cls.PAnchorBinding) } t, err := p.TimeAnchor() if err != nil { return nil, fmt.Errorf("failed to retrieve the time anchor value for predicate %q in binding %q with error %v", p, cls.PAnchorBinding, err) } c := &table.Cell{T: t} r[cls.PAnchorBinding] = c if !validBinding(cls.PAnchorBinding, c) { return nil, nil } } if cls.PAnchorAlias != "" { if p.Type() != predicate.Temporal { return nil, fmt.Errorf("cannot retrieve the time anchor value for non temporal predicate %q in binding %q", p, cls.PAnchorAlias) } t, err := p.TimeAnchor() if err != nil { return nil, fmt.Errorf("failed to retrieve the time anchor value for predicate %q in binding %q with error %v", p, cls.PAnchorAlias, err) } c := &table.Cell{T: t} r[cls.PAnchorAlias] = c if !validBinding(cls.PAnchorAlias, c) { return nil, nil } } // Object related bindings. if cls.OBinding != "" { // Extract the object type. c, err := objectToCell(o) if err != nil { return nil, err } r[cls.OBinding] = c if !validBinding(cls.OBinding, c) { return nil, nil } } if cls.OAlias != "" { // Extract the object type. c, err := objectToCell(o) if err != nil { return nil, err } r[cls.OAlias] = c if !validBinding(cls.OAlias, c) { return nil, nil } } if cls.OTypeAlias != "" { n, err := o.Node() if err != nil { return nil, err } c := &table.Cell{S: table.CellString(n.Type().String())} r[cls.OTypeAlias] = c if !validBinding(cls.OTypeAlias, c) { return nil, nil } } if cls.OIDAlias != "" { n, err := o.Node() if err == nil { r[cls.OIDAlias] = &table.Cell{S: table.CellString(n.ID().String())} } else { p, err := o.Predicate() if err != nil { return nil, err } c := &table.Cell{S: table.CellString(string(p.ID()))} r[cls.OIDAlias] = c if !validBinding(cls.OIDAlias, c) { return nil, nil } } } if cls.OAnchorBinding != "" { p, err := o.Predicate() if err != nil { return nil, err } ts, err := p.TimeAnchor() if err != nil { return nil, err } c := &table.Cell{T: ts} r[cls.OAnchorBinding] = c if !validBinding(cls.OAnchorBinding, c) { return nil, nil } } if cls.OAnchorAlias != "" { p, err := o.Predicate() if err != nil { return nil, err } ts, err := p.TimeAnchor() if err != nil { return nil, err } c := &table.Cell{T: ts} r[cls.OAnchorAlias] = c if !validBinding(cls.OAnchorAlias, c) { return nil, nil } } return r, nil }