Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 49 additions & 5 deletions bql/grammar/grammar.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func BQL() *Grammar {
NewTokenType(lexer.ItemQuery),
NewSymbol("VARS"),
NewTokenType(lexer.ItemFrom),
NewSymbol("GRAPHS"),
NewSymbol("INPUT_GRAPHS"),
NewSymbol("WHERE"),
NewSymbol("GROUP_BY"),
NewSymbol("ORDER_BY"),
Expand All @@ -49,7 +49,7 @@ func BQL() *Grammar {
NewTokenType(lexer.ItemInsert),
NewTokenType(lexer.ItemData),
NewTokenType(lexer.ItemInto),
NewSymbol("GRAPHS"),
NewSymbol("OUTPUT_GRAPHS"),
NewTokenType(lexer.ItemLBracket),
NewTokenType(lexer.ItemNode),
NewTokenType(lexer.ItemPredicate),
Expand All @@ -64,7 +64,7 @@ func BQL() *Grammar {
NewTokenType(lexer.ItemDelete),
NewTokenType(lexer.ItemData),
NewTokenType(lexer.ItemFrom),
NewSymbol("GRAPHS"),
NewSymbol("INPUT_GRAPHS"),
NewTokenType(lexer.ItemLBracket),
NewTokenType(lexer.ItemNode),
NewTokenType(lexer.ItemPredicate),
Expand Down Expand Up @@ -93,9 +93,9 @@ func BQL() *Grammar {
NewTokenType(lexer.ItemConstruct),
NewSymbol("CONSTRUCT_FACTS"),
NewTokenType(lexer.ItemInto),
NewSymbol("GRAPHS"),
NewSymbol("OUTPUT_GRAPHS"),
NewTokenType(lexer.ItemFrom),
NewSymbol("GRAPHS"),
NewSymbol("INPUT_GRAPHS"),
NewSymbol("WHERE"),
NewSymbol("HAVING"),
NewTokenType(lexer.ItemSemicolon),
Expand Down Expand Up @@ -194,6 +194,42 @@ func BQL() *Grammar {
},
{},
},
"INPUT_GRAPHS": []*Clause{
{
Elements: []Element{
NewTokenType(lexer.ItemBinding),
NewSymbol("MORE_INPUT_GRAPHS"),
},
},
},
"MORE_INPUT_GRAPHS": []*Clause{
{
Elements: []Element{
NewTokenType(lexer.ItemComma),
NewTokenType(lexer.ItemBinding),
NewSymbol("MORE_INPUT_GRAPHS"),
},
},
{},
},
"OUTPUT_GRAPHS": []*Clause{
{
Elements: []Element{
NewTokenType(lexer.ItemBinding),
NewSymbol("MORE_OUTPUT_GRAPHS"),
},
},
},
"MORE_OUTPUT_GRAPHS": []*Clause{
{
Elements: []Element{
NewTokenType(lexer.ItemComma),
NewTokenType(lexer.ItemBinding),
NewSymbol("MORE_OUTPUT_GRAPHS"),
},
},
{},
},
"WHERE": []*Clause{
{
Elements: []Element{
Expand Down Expand Up @@ -919,6 +955,14 @@ func SemanticBQL() *Grammar {
graphSymbols := []semantic.Symbol{"GRAPHS", "MORE_GRAPHS"}
setElementHook(semanticBQL, graphSymbols, semantic.GraphAccumulatorHook(), nil)

// Add graph binding collection to INPUT_GRAPHS and MORE_INPUT_GRAPHS clauses.
inputGraphSymbols := []semantic.Symbol{"INPUT_GRAPHS", "MORE_INPUT_GRAPHS"}
setElementHook(semanticBQL, inputGraphSymbols, semantic.InputGraphAccumulatorHook(), nil)

// Add graph binding collection to OUTPUT_GRAPHS and MORE_OUTPUT_GRAPHS clauses.
outputGraphSymbols := []semantic.Symbol{"OUTPUT_GRAPHS", "MORE_OUTPUT_GRAPHS"}
setElementHook(semanticBQL, outputGraphSymbols, semantic.OutputGraphAccumulatorHook(), nil)

// Insert and Delete semantic hooks addition.
insertSymbols := []semantic.Symbol{
"INSERT_OBJECT", "INSERT_DATA", "DELETE_OBJECT", "DELETE_DATA",
Expand Down
73 changes: 49 additions & 24 deletions bql/grammar/grammar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package grammar

import (
"reflect"
"testing"

"github.com/google/badwolf/bql/semantic"
Expand Down Expand Up @@ -255,36 +256,54 @@ func TestRejectByParse(t *testing.T) {
}
}

func TestAcceptOpsByParseAndSemantic(t *testing.T) {
func TestAcceptGraphOpsByParseAndSemantic(t *testing.T) {
var empty []string
table := []struct {
query string
graphs int
triples int
query string
graphs []string
inputGraphs []string
outputGraphs []string
triples int
}{
// Insert data.
{`insert data into ?a {/_<foo> "bar"@[1975-01-01T00:01:01.999999999Z] /_<foo>};`, 1, 1},
{`insert data into ?a {/_<foo> "bar"@[] "bar"@[1975-01-01T00:01:01.999999999Z]};`, 1, 1},
{`insert data into ?a {/_<foo> "bar"@[] "yeah"^^type:text};`, 1, 1},
// Insert into multiple graphs.
{`insert data into ?a,?b,?c {/_<foo> "bar"@[] /_<foo>};`, 3, 1},
// Create graphs. All graphs are regular graphs.
{`create graph ?foo1, ?bar1;`, []string{"?foo1", "?bar1"}, empty, empty, 0},
// Drop graphs. All graphs are regular graphs.
{`drop graph ?foo2, ?bar2;`, []string{"?foo2", "?bar2"}, empty, empty, 0},

// Insert data. All graphs are output graphs.
{`insert data into ?a {/_<foo> "bar"@[1975-01-01T00:01:01.999999999Z] /_<foo>};`, empty, empty, []string{"?a"}, 1},
{`insert data into ?a {/_<foo> "bar"@[] "bar"@[1975-01-01T00:01:01.999999999Z]};`, empty, empty, []string{"?a"}, 1},
{`insert data into ?a {/_<foo> "bar"@[] "yeah"^^type:text};`, empty, empty, []string{"?a"}, 1},
// Insert into multiple output graphs.
{`insert data into ?a,?b,?c {/_<foo> "bar"@[] /_<foo>};`, empty, empty, []string{"?a", "?b", "?c"}, 1},
// Insert multiple data.
{`insert data into ?a {/_<foo> "bar"@[] /_<foo> .
/_<foo> "bar"@[] "bar"@[1975-01-01T00:01:01.999999999Z] .
/_<foo> "bar"@[] "yeah"^^type:text};`, 1, 3},
// Delete data.
{`delete data from ?a {/_<foo> "bar"@[] /_<foo>};`, 1, 1},
{`delete data from ?a {/_<foo> "bar"@[] "bar"@[1975-01-01T00:01:01.999999999Z]};`, 1, 1},
{`delete data from ?a {/_<foo> "bar"@[] "yeah"^^type:text};`, 1, 1},
// Delete from multiple graphs.
{`delete data from ?a,?b,?c {/_<foo> "bar"@[1975-01-01T00:01:01.999999999Z] /_<foo>};`, 3, 1},
/_<foo> "bar"@[] "yeah"^^type:text};`, empty, empty, []string{"?a"}, 3},

// Delete data. All graphs are input graphs.
{`delete data from ?a {/_<foo> "bar"@[] /_<foo>};`, empty, []string{"?a"}, empty, 1},
{`delete data from ?a {/_<foo> "bar"@[] "bar"@[1975-01-01T00:01:01.999999999Z]};`, empty, []string{"?a"}, empty, 1},
{`delete data from ?a {/_<foo> "bar"@[] "yeah"^^type:text};`, empty, []string{"?a"}, empty, 1},
// Delete from multiple input graphs.
{`delete data from ?a,?b,?c {/_<foo> "bar"@[1975-01-01T00:01:01.999999999Z] /_<foo>};`, empty, []string{"?a", "?b", "?c"}, empty, 1},
// Delete multiple data.
{`delete data from ?a {/_<foo> "bar"@[] /_<foo> .
/_<foo> "bar"@[] "bar"@[1975-01-01T00:01:01.999999999Z] .
/_<foo> "bar"@[] "yeah"^^type:text};`, 1, 3},
// Create graphs.
{`create graph ?foo;`, 1, 0},
// Drop graphs.
{`drop graph ?foo, ?bar;`, 2, 0},
/_<foo> "bar"@[] "yeah"^^type:text};`, empty, []string{"?a"}, empty, 3},

// Construct data. Graphs can be input or output graphs.
{`construct {?s "predicate_1"@[] ?o1;
"predicate_2"@[] ?o2} into ?a from ?b where {?s "old_predicate_1"@[,] ?o1.
?s "old_predicate_2"@[,] ?o2.
?s "old_predicate_3"@[,] ?o3};`,
empty, []string{"?b"}, []string{"?a"}, 0},
// construct data into multiple output graphs from multple input graphs.
{`construct {?s "predicate_1"@[] ?o1;
"predicate_2"@[] ?o2} into ?a, ?b from ?c, ?d where {?s "old_predicate_1"@[,] ?o1.
?s "old_predicate_2"@[,] ?o2.
?s "old_predicate_3"@[,] ?o3};`,
empty, []string{"?c", "?d"}, []string{"?a", "?b"}, 0},
}
p, err := NewParser(SemanticBQL())
if err != nil {
Expand All @@ -295,8 +314,14 @@ func TestAcceptOpsByParseAndSemantic(t *testing.T) {
if err := p.Parse(NewLLk(entry.query, 1), st); err != nil {
t.Errorf("Parser.consume: Failed to accept entry %q with error %v", entry, err)
}
if got, want := len(st.GraphNames()), entry.graphs; got != want {
t.Errorf("Parser.consume: Failed to collect right number of graphs for case %v; got %d, want %d", entry, got, want)
if got, want := st.GraphNames(), entry.graphs; !reflect.DeepEqual(got, want) {
t.Errorf("Parser.consume: Failed to collect the right graphs for case %v; got %d, want %d", entry, got, want)
}
if got, want := st.InputGraphNames(), entry.inputGraphs; !reflect.DeepEqual(got, want) {
t.Errorf("Parser.consume: Failed to collect the right input graphs for case %v; got %d, want %d", entry, got, want)
}
if got, want := st.OutputGraphNames(), entry.outputGraphs; !reflect.DeepEqual(got, want) {
t.Errorf("Parser.consume: Failed to collect the right output graphs for case %v; got %d, want %d", entry, got, want)
}
if got, want := len(st.Data()), entry.triples; got != want {
t.Errorf("Parser.consume: Failed to collect right number of triples for case %v; got %d, want %d", entry, got, want)
Expand Down
14 changes: 7 additions & 7 deletions bql/planner/planner.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func update(ctx context.Context, stm *semantic.Statement, store storage.Store, f
errs = append(errs, err.Error())
}

for _, graphBinding := range stm.GraphNames() {
for _, graphBinding := range stm.OutputGraphNames() {
wg.Add(1)
go func(graph string) {
defer wg.Done()
Expand Down Expand Up @@ -191,7 +191,7 @@ func (p *insertPlan) Execute(ctx context.Context) (*table.Table, error) {
// String returns a readable description of the execution plan.
func (p *insertPlan) String() string {
b := bytes.NewBufferString("INSERT plan:\n\n")
for _, g := range p.stm.Graphs() {
for _, g := range p.stm.OutputGraphs() {
b.WriteString(fmt.Sprintf("store(%q).Graph(%q).AddTriples(_, data)\n", p.store.Name(nil), g))
}
b.WriteString("where data:\n")
Expand Down Expand Up @@ -228,7 +228,7 @@ func (p *deletePlan) Execute(ctx context.Context) (*table.Table, error) {
// String returns a readable description of the execution plan.
func (p *deletePlan) String() string {
b := bytes.NewBufferString("DELETE plan:\n\n")
for _, g := range p.stm.Graphs() {
for _, g := range p.stm.InputGraphs() {
b.WriteString(fmt.Sprintf("store(%q).Graph(%q).RemoveTriples(_, data)\n", p.store.Name(nil), g))
}
b.WriteString("where data:\n")
Expand Down Expand Up @@ -270,7 +270,7 @@ func newQueryPlan(ctx context.Context, store storage.Store, stm *semantic.Statem
stm: stm,
store: store,
bndgs: bs,
grfsNames: stm.GraphNames(),
grfsNames: stm.InputGraphNames(),
cls: stm.SortedGraphPatternClauses(),
tbl: t,
chanSize: chanSize,
Expand Down Expand Up @@ -524,7 +524,7 @@ func (p *queryPlan) filterOnExistence(ctx context.Context, cls *semantic.GraphCl
return fmt.Errorf("failed to fully specify clause %v for row %+v", cls, r)
}
exist := false
for _, g := range p.stm.Graphs() {
for _, g := range p.stm.InputGraphs() {
t, err := triple.New(sbj, prd, obj)
if err != nil {
return err
Expand Down Expand Up @@ -708,12 +708,12 @@ func (p *queryPlan) limit() {
func (p *queryPlan) Execute(ctx context.Context) (*table.Table, error) {
// Fetch and catch graph instances.
trace(p.tracer, func() []string {
return []string{fmt.Sprintf("Caching graph instances for graphs %v", p.stm.GraphNames())}
return []string{fmt.Sprintf("Caching graph instances for graphs %v", p.stm.InputGraphNames())}
})
if err := p.stm.Init(ctx, p.store); err != nil {
return nil, err
}
p.grfs = p.stm.Graphs()
p.grfs = p.stm.InputGraphs()
// Retrieve the data.
lo := p.stm.GlobalLookupOptions()
trace(p.tracer, func() []string {
Expand Down
18 changes: 9 additions & 9 deletions bql/planner/planner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,14 +501,14 @@ func TestPlannerQuery(t *testing.T) {
}
tbl, err := plnr.Execute(ctx)
if err != nil {
t.Errorf("planner.Excecute failed for query %q with error %v", entry.q, err)
t.Errorf("planner.Execute failed for query %q with error %v", entry.q, err)
continue
}
if got, want := len(tbl.Bindings()), entry.nbs; got != want {
t.Errorf("tbl.Bindings returned the wrong number of bindings for %q; got %d, want %d", entry.q, got, want)
}
if got, want := len(tbl.Rows()), entry.nrws; got != want {
t.Errorf("planner.Excecute failed to return the expected number of rows for query %q; got %d want %d\nGot:\n%v\n", entry.q, got, want, tbl)
t.Errorf("planner.Execute failed to return the expected number of rows for query %q; got %d want %d\nGot:\n%v\n", entry.q, got, want, tbl)
}
}
}
Expand Down Expand Up @@ -552,13 +552,13 @@ func TestTreeTraversalToRoot(t *testing.T) {
}
tbl, err := plnr.Execute(ctx)
if err != nil {
t.Errorf("planner.Excecute failed for query %q with error %v", traversalQuery, err)
t.Errorf("planner.Execute failed for query %q with error %v", traversalQuery, err)
}
if got, want := len(tbl.Bindings()), 1; got != want {
t.Errorf("tbl.Bindings returned the wrong number of bindings for %q; got %d, want %d", traversalQuery, got, want)
}
if got, want := len(tbl.Rows()), 1; got != want {
t.Errorf("planner.Excecute failed to return the expected number of rows for query %q; got %d want %d\nGot:\n%v\n", traversalQuery, got, want, tbl)
t.Errorf("planner.Execute failed to return the expected number of rows for query %q; got %d want %d\nGot:\n%v\n", traversalQuery, got, want, tbl)
}
}

Expand Down Expand Up @@ -599,13 +599,13 @@ func TestChaining(t *testing.T) {
}
tbl, err := plnr.Execute(ctx)
if err != nil {
t.Errorf("planner.Excecute failed for query %q with error %v", traversalQuery, err)
t.Errorf("planner.Execute failed for query %q with error %v", traversalQuery, err)
}
if got, want := len(tbl.Bindings()), 1; got != want {
t.Errorf("tbl.Bindings returned the wrong number of bindings for %q; got %d, want %d", traversalQuery, got, want)
}
if got, want := len(tbl.Rows()), 1; got != want {
t.Errorf("planner.Excecute failed to return the expected number of rows for query %q; got %d want %d\nGot:\n%v\n", traversalQuery, got, want, tbl)
t.Errorf("planner.Execute failed to return the expected number of rows for query %q; got %d want %d\nGot:\n%v\n", traversalQuery, got, want, tbl)
}
}

Expand Down Expand Up @@ -657,13 +657,13 @@ func TestReificationResolutionIssue70(t *testing.T) {
}
tbl, err := plnr.Execute(ctx)
if err != nil {
t.Fatalf("planner.Excecute failed for query %q with error %v", query, err)
t.Fatalf("planner.Execute failed for query %q with error %v", query, err)
}
if got, want := len(tbl.Bindings()), 2; got != want {
t.Errorf("tbl.Bindings returned the wrong number of bindings for %q; got %d, want %d", query, got, want)
}
if got, want := len(tbl.Rows()), 1; got != want {
t.Errorf("planner.Excecute failed to return the expected number of rows for query %q; got %d want %d\nGot:\n%v\n", query, got, want, tbl)
t.Errorf("planner.Execute failed to return the expected number of rows for query %q; got %d want %d\nGot:\n%v\n", query, got, want, tbl)
}
}

Expand All @@ -688,7 +688,7 @@ func benchmarkQuery(query string, b *testing.B) {
}
_, err = plnr.Execute(ctx)
if err != nil {
b.Errorf("planner.Excecute failed for query %q with error %v", query, err)
b.Errorf("planner.Execute failed for query %q with error %v", query, err)
}
}
}
Expand Down
Loading