func TestDataAccessUnfeasibleSimpleExist(t *testing.T) { ctx := context.Background() g, err := getTestStore(t, testImmutatbleTriples).Graph(ctx, "?test") if err != nil { t.Fatal(err) } unknown, err := node.Parse("/unknown<unknown>") if err != nil { t.Fatal(err) } tt := getTestTriples(t, testImmutatbleTriples) s, p, o := unknown, tt[0].Predicate(), tt[0].Object() clsNotOK := &semantic.GraphClause{ S: s, P: p, O: o, } tplNotOK, err := triple.New(s, p, o) if err != nil { t.Fatal(err) } unfeasible, tbl, err := simpleExist(ctx, []storage.Graph{g}, clsNotOK, tplNotOK) if err != nil { t.Errorf("simpleExist should have not failed with error %v", err) } if !unfeasible { t.Error(errors.New("simpleExist should have return an unfeasible table instead")) } if got, want := tbl.NumRows(), 0; got != want { t.Errorf("simpleExist failed to return the right number of rows: got %d, want %d", got, want) } }
// dataAccumulator creates a element hook that tracks fully formed triples and // adds them to the Statement when fully formed. func dataAccumulator(b literal.Builder) ElementHook { var ( hook ElementHook s *node.Node p *predicate.Predicate o *triple.Object ) hook = func(st *Statement, ce ConsumedElement) (ElementHook, error) { if ce.IsSymbol() { return hook, nil } tkn := ce.Token() if tkn.Type != lexer.ItemNode && tkn.Type != lexer.ItemPredicate && tkn.Type != lexer.ItemLiteral { return hook, nil } if s == nil { if tkn.Type != lexer.ItemNode { return nil, fmt.Errorf("hook.DataAccumulator requires a node to create a subject, got %v instead", tkn) } tmp, err := node.Parse(tkn.Text) if err != nil { return nil, err } s = tmp return hook, nil } if p == nil { if tkn.Type != lexer.ItemPredicate { return nil, fmt.Errorf("hook.DataAccumulator requires a predicate to create a predicate, got %v instead", tkn) } tmp, err := predicate.Parse(tkn.Text) if err != nil { return nil, err } p = tmp return hook, nil } if o == nil { tmp, err := triple.ParseObject(tkn.Text, b) if err != nil { return nil, err } o = tmp trpl, err := triple.New(s, p, o) if err != nil { return nil, err } st.AddData(trpl) s, p, o = nil, nil, nil return hook, nil } return nil, fmt.Errorf("hook.DataAccumulator has failed to flush the triple %s, %s, %s", s, p, o) } return hook }
// newTriple creates new triple using the provided node IDs. func (r *randomGraph) newTriple(i, j int) (*triple.Triple, error) { s, err := r.newNode(i) if err != nil { return nil, err } o, err := r.newNode(j) if err != nil { return nil, err } return triple.New(s, r.predicate, triple.NewNodeObject(o)) }
// processClause retrieves the triples for the provided triple given the // information available. func (p *queryPlan) processClause(ctx context.Context, cls *semantic.GraphClause, lo *storage.LookupOptions) (bool, error) { // This method decides how to process the clause based on the current // list of bindings solved and data available. if cls.Specificity() == 3 { t, err := triple.New(cls.S, cls.P, cls.O) if err != nil { return false, err } b, tbl, err := simpleExist(ctx, p.grfs, cls, t) if err != nil { return false, err } if err := p.tbl.AppendTable(tbl); err != nil { return b, err } return b, nil } exist, total := 0, 0 for _, b := range cls.Bindings() { total++ if p.tbl.HasBinding(b) { exist++ } } if exist == 0 { // Data is new. tbl, err := simpleFetch(ctx, p.grfs, cls, lo, p.chanSize) if err != nil { return false, err } if len(p.tbl.Bindings()) > 0 { return false, p.tbl.DotProduct(tbl) } return false, p.tbl.AppendTable(tbl) } if exist > 0 && exist < total { // Data is partially bound, retrieve data either extends the row with the // new bindings or filters it out if now new bindings are available. return false, p.specifyClauseWithTable(ctx, cls, lo) } if exist > 0 && exist == total { // Since all bindings in the clause are already solved, the clause becomes a // fully specified triple. If the triple does not exist the row will be // deleted. return false, p.filterOnExistence(ctx, cls, lo) } // Something is wrong with the code. return false, fmt.Errorf("queryPlan.processClause(%v) should have never failed to resolve the clause", cls) }
// simpleFetch returns a table containing the data specified by the graph // clause by querying the provided stora. Will return an error if it had poblems // retrieveing the data. func simpleFetch(gs []storage.Graph, cls *semantic.GraphClause, lo *storage.LookupOptions) (*table.Table, error) { s, p, o := cls.S, cls.P, cls.O lo = updateTimeBounds(lo, cls) tbl, err := table.New(cls.Bindings()) if err != nil { return nil, err } if s != nil && p != nil && o != nil { // Fully qualified triple. t, err := triple.New(s, p, o) if err != nil { return nil, err } for _, g := range gs { b, err := g.Exist(t) if err != nil { return nil, err } if b { ts := make(chan *triple.Triple, 1) ts <- t close(ts) if err := addTriples(ts, cls, tbl); err != nil { return nil, err } } } return tbl, nil } if s != nil && p != nil && o == nil { // SP request. for _, g := range gs { os, err := g.Objects(s, p, lo) if err != nil { return nil, err } var ros []*triple.Object for o := range os { ros = append(ros, o) } ts := make(chan *triple.Triple, len(ros)) for _, o := range ros { t, err := triple.New(s, p, o) if err != nil { return nil, err } ts <- t } close(ts) if err := addTriples(ts, cls, tbl); err != nil { return nil, err } } return tbl, nil } if s != nil && p == nil && o != nil { // SO request. for _, g := range gs { ps, err := g.PredicatesForSubjectAndObject(s, o, lo) if err != nil { return nil, err } var rps []*predicate.Predicate for p := range ps { rps = append(rps, p) } ts := make(chan *triple.Triple, len(rps)) for _, p := range rps { t, err := triple.New(s, p, o) if err != nil { return nil, err } ts <- t } close(ts) if err := addTriples(ts, cls, tbl); err != nil { return nil, err } } return tbl, nil } if s == nil && p != nil && o != nil { // PO request. for _, g := range gs { ss, err := g.Subjects(p, o, lo) if err != nil { return nil, err } var rss []*node.Node for s := range ss { rss = append(rss, s) } ts := make(chan *triple.Triple, len(rss)) for _, s := range rss { t, err := triple.New(s, p, o) if err != nil { return nil, err } ts <- t } close(ts) if err := addTriples(ts, cls, tbl); err != nil { return nil, err } } return tbl, nil } if s != nil && p == nil && o == nil { // S request. for _, g := range gs { ts, err := g.TriplesForSubject(s, lo) if err != nil { return nil, err } if err := addTriples(ts, cls, tbl); err != nil { return nil, err } } return tbl, nil } if s == nil && p != nil && o == nil { // P request. for _, g := range gs { ts, err := g.TriplesForPredicate(p, lo) if err != nil { return nil, err } if err := addTriples(ts, cls, tbl); err != nil { return nil, err } } return tbl, nil } if s == nil && p == nil && o != nil { // O request. for _, g := range gs { ts, err := g.TriplesForObject(o, lo) if err != nil { return nil, err } if err := addTriples(ts, cls, tbl); err != nil { return nil, err } } return tbl, nil } if s == nil && p == nil && o == nil { // Full data request. for _, g := range gs { ts, err := g.Triples() if err != nil { return nil, err } if err := addTriples(ts, cls, tbl); err != nil { return nil, err } } return tbl, nil } return nil, fmt.Errorf("planner.simpleFetch could not recognize request in clause %v", cls) }
// filterOnExistance removes rows based on the existance of the fully qualified // triple after the biding of the clause. func (p *queryPlan) filterOnExistance(cls *semantic.GraphClause, lo *storage.LookupOptions) error { for idx, r := range p.tbl.Rows() { sbj, prd, obj := cls.S, cls.P, cls.O // Attempt to rebind the subject. if sbj == nil && p.tbl.HasBinding(cls.SBinding) { v, ok := r[cls.SBinding] if !ok { return fmt.Errorf("row %+v misses binding %q", r, cls.SBinding) } if v.N == nil { return fmt.Errorf("binding %q requires a node, got %+v instead", cls.SBinding, v) } sbj = v.N } if sbj == nil && p.tbl.HasBinding(cls.SAlias) { v, ok := r[cls.SAlias] if !ok { return fmt.Errorf("row %+v misses binding %q", r, cls.SAlias) } if v.N == nil { return fmt.Errorf("binding %q requires a node, got %+v instead", cls.SAlias, v) } sbj = v.N } // Attempt to rebind the predicate. if prd == nil && p.tbl.HasBinding(cls.PBinding) { v, ok := r[cls.PBinding] if !ok { return fmt.Errorf("row %+v misses binding %q", r, cls.PBinding) } if v.P == nil { return fmt.Errorf("binding %q requires a predicate, got %+v instead", cls.PBinding, v) } prd = v.P } if prd == nil && p.tbl.HasBinding(cls.PAlias) { v, ok := r[cls.PAlias] if !ok { return fmt.Errorf("row %+v misses binding %q", r, cls.SAlias) } if v.N == nil { return fmt.Errorf("binding %q requires a predicate, got %+v instead", cls.SAlias, v) } prd = v.P } // Attempt to rebind the object. if obj == nil && p.tbl.HasBinding(cls.PBinding) { v, ok := r[cls.OBinding] if !ok { return fmt.Errorf("row %+v misses binding %q", r, cls.OBinding) } if v.P == nil { return fmt.Errorf("binding %q requires a object, got %+v instead", cls.OBinding, v) } co, err := cellToObject(v) if err != nil { return err } obj = co } if obj == nil && p.tbl.HasBinding(cls.OAlias) { v, ok := r[cls.OAlias] if !ok { return fmt.Errorf("row %+v misses binding %q", r, cls.OAlias) } if v.N == nil { return fmt.Errorf("binding %q requires a object, got %+v instead", cls.OAlias, v) } co, err := cellToObject(v) if err != nil { return err } obj = co } // Attempt to filter. if sbj == nil || prd == nil || obj == nil { return fmt.Errorf("failed to fully specify clause %v for row %+v", cls, r) } for _, g := range p.stm.Graphs() { t, err := triple.New(sbj, prd, obj) if err != nil { return err } gph, err := p.store.Graph(g) if err != nil { return err } b, err := gph.Exist(t) if err != nil { return err } if b { p.tbl.DeleteRow(idx) } } } return nil }
// newTriple creates a new triple given the parent and the descendant as an object. func (t *treeGenerator) newTriple(parent, descendant *node.Node) (*triple.Triple, error) { return triple.New(parent, t.predicate, triple.NewNodeObject(descendant)) }
// simpleFetch returns a table containing the data specified by the graph // clause by querying the provided stora. Will return an error if it had poblems // retrieveing the data. func simpleFetch(ctx context.Context, gs []storage.Graph, cls *semantic.GraphClause, lo *storage.LookupOptions, chanSize int) (*table.Table, error) { s, p, o := cls.S, cls.P, cls.O lo = updateTimeBounds(lo, cls) tbl, err := table.New(cls.Bindings()) if err != nil { return nil, err } if s != nil && p != nil && o != nil { // Fully qualified triple. t, err := triple.New(s, p, o) if err != nil { return nil, err } for _, g := range gs { b, err := g.Exist(ctx, t) if err != nil { return nil, err } if b { ts := make(chan *triple.Triple, 1) ts <- t close(ts) if err := addTriples(ts, cls, tbl); err != nil { return nil, err } } } return tbl, nil } if s != nil && p != nil && o == nil { // SP request. for _, g := range gs { var ( oErr error aErr error lErr error wg sync.WaitGroup ) wg.Add(2) os := make(chan *triple.Object, chanSize) go func() { defer wg.Done() oErr = g.Objects(ctx, s, p, lo, os) }() ts := make(chan *triple.Triple, chanSize) go func() { defer wg.Done() aErr = addTriples(ts, cls, tbl) }() for o := range os { if lErr != nil { // Drain the channel to avoid leaking goroutines. continue } t, err := triple.New(s, p, o) if err != nil { lErr = err continue } ts <- t } close(ts) wg.Wait() if oErr != nil { return nil, oErr } if aErr != nil { return nil, aErr } if lErr != nil { return nil, lErr } } return tbl, nil } if s != nil && p == nil && o != nil { // SO request. for _, g := range gs { var ( pErr error aErr error lErr error wg sync.WaitGroup ) wg.Add(2) ps := make(chan *predicate.Predicate, chanSize) go func() { defer wg.Done() pErr = g.PredicatesForSubjectAndObject(ctx, s, o, lo, ps) }() ts := make(chan *triple.Triple, chanSize) go func() { defer wg.Done() aErr = addTriples(ts, cls, tbl) }() for p := range ps { if lErr != nil { // Drain the channel to avoid leaking goroutines. continue } t, err := triple.New(s, p, o) if err != nil { lErr = err continue } ts <- t } close(ts) wg.Wait() if pErr != nil { return nil, pErr } if aErr != nil { return nil, aErr } if lErr != nil { return nil, lErr } } return tbl, nil } if s == nil && p != nil && o != nil { // PO request. for _, g := range gs { var ( pErr error aErr error lErr error wg sync.WaitGroup ) wg.Add(2) ss := make(chan *node.Node, chanSize) go func() { defer wg.Done() pErr = g.Subjects(ctx, p, o, lo, ss) }() ts := make(chan *triple.Triple, chanSize) go func() { defer wg.Done() aErr = addTriples(ts, cls, tbl) }() for s := range ss { if lErr != nil { // Drain the channel to avoid leaking goroutines. continue } t, err := triple.New(s, p, o) if err != nil { lErr = err continue } ts <- t } close(ts) wg.Wait() if pErr != nil { return nil, pErr } if aErr != nil { return nil, aErr } if lErr != nil { return nil, lErr } } return tbl, nil } if s != nil && p == nil && o == nil { // S request. for _, g := range gs { var ( tErr error aErr error wg sync.WaitGroup ) ts := make(chan *triple.Triple, chanSize) wg.Add(1) go func() { defer wg.Done() tErr = g.TriplesForSubject(ctx, s, lo, ts) }() aErr = addTriples(ts, cls, tbl) wg.Wait() if tErr != nil { return nil, tErr } if aErr != nil { return nil, aErr } } return tbl, nil } if s == nil && p != nil && o == nil { // P request. for _, g := range gs { var ( tErr error aErr error wg sync.WaitGroup ) ts := make(chan *triple.Triple, chanSize) wg.Add(1) go func() { defer wg.Done() tErr = g.TriplesForPredicate(ctx, p, lo, ts) }() aErr = addTriples(ts, cls, tbl) wg.Wait() if tErr != nil { return nil, tErr } if aErr != nil { return nil, aErr } } return tbl, nil } if s == nil && p == nil && o != nil { // O request. for _, g := range gs { var ( tErr error wg sync.WaitGroup ) ts := make(chan *triple.Triple, chanSize) wg.Add(1) go func() { defer wg.Done() tErr = g.TriplesForObject(ctx, o, lo, ts) }() aErr := addTriples(ts, cls, tbl) wg.Wait() if tErr != nil { return nil, tErr } if aErr != nil { return nil, aErr } } return tbl, nil } if s == nil && p == nil && o == nil { // Full data request. for _, g := range gs { var ( tErr error aErr error wg sync.WaitGroup ) ts := make(chan *triple.Triple, chanSize) wg.Add(1) go func() { defer wg.Done() tErr = g.Triples(ctx, ts) }() aErr = addTriples(ts, cls, tbl) wg.Wait() if tErr != nil { return nil, tErr } if aErr != nil { return nil, aErr } } return tbl, nil } return nil, fmt.Errorf("planner.simpleFetch could not recognize request in clause %v", cls) }