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
58 changes: 54 additions & 4 deletions bql/grammar/grammar.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,19 @@ func BQL() *Grammar {
NewTokenType(lexer.ItemSemicolon),
},
},
{
Elements: []Element{
NewTokenType(lexer.ItemDeconstruct),
NewSymbol("DECONSTRUCT_FACTS"),
NewTokenType(lexer.ItemIn),
NewSymbol("OUTPUT_GRAPHS"),
NewTokenType(lexer.ItemFrom),
NewSymbol("INPUT_GRAPHS"),
NewSymbol("WHERE"),
NewSymbol("HAVING"),
NewTokenType(lexer.ItemSemicolon),
},
},
},
"CREATE_GRAPHS": []*Clause{
{
Expand Down Expand Up @@ -879,6 +892,42 @@ func BQL() *Grammar {
},
{},
},
"DECONSTRUCT_FACTS": []*Clause{
{
Elements: []Element{
NewTokenType(lexer.ItemLBracket),
NewSymbol("DECONSTRUCT_TRIPLES"),
NewTokenType(lexer.ItemRBracket),
},
},
},
"DECONSTRUCT_TRIPLES": []*Clause{
{
Elements: []Element{
NewTokenType(lexer.ItemNode),
NewSymbol("CONSTRUCT_PREDICATE"),
NewSymbol("CONSTRUCT_OBJECT"),
NewSymbol("MORE_DECONSTRUCT_TRIPLES"),
},
},
{
Elements: []Element{
NewTokenType(lexer.ItemBinding),
NewSymbol("CONSTRUCT_PREDICATE"),
NewSymbol("CONSTRUCT_OBJECT"),
NewSymbol("MORE_DECONSTRUCT_TRIPLES"),
},
},
},
"MORE_DECONSTRUCT_TRIPLES": []*Clause{
{
Elements: []Element{
NewTokenType(lexer.ItemDot),
NewSymbol("DECONSTRUCT_TRIPLES"),
},
},
{},
},
}
}

Expand Down Expand Up @@ -1001,14 +1050,15 @@ func SemanticBQL() *Grammar {
})
setClauseHook(semanticBQL, []semantic.Symbol{"START"}, nil, semantic.GroupByBindingsChecker())

// CONSTRUCT clause semantic hooks.
// CONSTRUCT and DECONSTRUCT clauses semantic hooks.
setClauseHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_FACTS"}, semantic.InitWorkingConstructClauseHook(), semantic.TypeBindingClauseHook(semantic.Construct))
constructTriplesSymbols := []semantic.Symbol{"CONSTRUCT_TRIPLES", "MORE_CONSTRUCT_TRIPLES"}
setClauseHook(semanticBQL, constructTriplesSymbols, semantic.NextWorkingConstructClauseHook(), semantic.NextWorkingConstructClauseHook())
setClauseHook(semanticBQL, []semantic.Symbol{"DECONSTRUCT_FACTS"}, semantic.InitWorkingConstructClauseHook(), semantic.TypeBindingClauseHook(semantic.Deconstruct))
constructAndDeconstructTriplesSymbols := []semantic.Symbol{"CONSTRUCT_TRIPLES", "MORE_CONSTRUCT_TRIPLES", "DECONSTRUCT_TRIPLES", "MORE_DECONSTRUCT_TRIPLES"}
setClauseHook(semanticBQL, constructAndDeconstructTriplesSymbols, semantic.NextWorkingConstructClauseHook(), semantic.NextWorkingConstructClauseHook())
setClauseHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_PREDICATE"}, semantic.NextWorkingConstructPredicateObjectPairClauseHook(), nil)
setClauseHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_OBJECT"}, nil, semantic.NextWorkingConstructPredicateObjectPairClauseHook())

setElementHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_TRIPLES"}, semantic.ConstructSubjectHook(), nil)
setElementHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_TRIPLES", "DECONSTRUCT_TRIPLES"}, semantic.ConstructSubjectHook(), nil)
setElementHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_PREDICATE"}, semantic.ConstructPredicateHook(), nil)
setElementHook(semanticBQL, []semantic.Symbol{"CONSTRUCT_OBJECT"}, semantic.ConstructObjectHook(), nil)

Expand Down
115 changes: 111 additions & 4 deletions bql/grammar/grammar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,18 @@ func TestAcceptByParse(t *testing.T) {
from ?b where {?s "old_predicate_1"@[,] ?o1.
?s "old_predicate_2"@[,] ?o2.
?s "old_predicate_3"@[,] ?o3};`,
// Test Deconstruct clause.
`deconstruct {?s "new_predicate"@[] ?o} in ?a from ?b where {?s "old_predicate"@[,] ?o} having ?s = ?o;`,
`deconstruct {?s "new_predicate"@[] ?o} in ?a from ?b where {?s "old_predicate"@[,] ?o};`,
`deconstruct {?s ?p ?o.
?n "_subject"@[] ?s.
?n "_predicate"@[] ?p.
?n "_object"@[] ?o}
in ?a, ?b
from ?c, ?d
where {?n "_subject"@[] ?s.
?n "_predicate"@[] ?p.
?n "_object"@[] ?o};`,
}
p, err := NewParser(BQL())
if err != nil {
Expand Down Expand Up @@ -262,6 +274,39 @@ func TestRejectByParse(t *testing.T) {
from ?b
where {?s "old_predicate_1"@[,] ?o1.
?s "old_predicate_2"@[,] ?o2};`,
// Deconstruct clause without source.
`deconstruct {?s "foo"@[,] ?o} in ?a where{?s "foo"@[,] ?o} having ?s = ?o;`,
// Deconstruct clause without destination.
`deconstruct {?s "foo"@[,] ?o} from ?b where{?s "foo"@[,] ?o} having ?s = ?o;`,
// Deconstruct clause with badly formed blank node.
`deconstruct {?s ?p ?o.
_v "some_pred"@[] ?k}
in ?a
from ?b
where {?s "foo"@[,] ?o};`,
// Deconstruct clause with badly formed triple.
`deconstruct {?s ?p ?o.
_:v "some_pred"@[]}
in ?a
from ?b
where {?s "foo"@[,] ?o};`,
// Deconstruct clause with multiple predicate-object pairs.
`deconstruct {?s "predicate_1"@[] ?o1;
"predicate_1"@[] ?o1}
in ?a
from ?b
where {?s "old_predicate_1"@[,] ?o1.
?s "old_predicate_2"@[,] ?o2};`,
// Deconstruct clause with blank nodes.
`deconstruct {?s ?p ?o.
_:v "_subject"@[] ?s.
_:v "_predicate"@[] ?p.
_:v "_object"@[] ?o}
in ?a, ?b
from ?c, ?d
where {?n "_subject"@[] ?s.
?n "_predicate"@[] ?p.
?n "_object"@[] ?o};`,
}
p, err := NewParser(BQL())
if err != nil {
Expand Down Expand Up @@ -317,16 +362,48 @@ func TestAcceptGraphOpsByParseAndSemantic(t *testing.T) {
from ?b
where {?s "old_predicate_1"@[,] ?o1.
?s "old_predicate_2"@[,] ?o2.
?s "old_predicate_3"@[,] ?o3};`, empty, []string{"?b"}, []string{"?a"}, 0},
?s "old_predicate_3"@[,] ?o3};`,
empty,
[]string{"?b"},
[]string{"?a"},
0},

// construct data into multiple output graphs from multple input graphs.
// 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},
?s "old_predicate_3"@[,] ?o3};`,
empty,
[]string{"?c", "?d"},
[]string{"?a", "?b"},
0},

// Deconstruct data. Graphs can be input or output graphs.
{`deconstruct {?s "predicate_1"@[] ?o1}
in ?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},

// Deconstruct data at multiple output graphs from multple input graphs.
{`deconstruct {?s "predicate_1"@[] ?o1}
in ?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 Down Expand Up @@ -461,7 +538,7 @@ func TestSemanticStatementGraphClausesLengthCorrectness(t *testing.T) {
}
}

func TestSemanticStatementConstructClausesLengthCorrectness(t *testing.T) {
func TestSemanticStatementConstructDeconstructClausesLengthCorrectness(t *testing.T) {
table := []struct {
query string
want int
Expand All @@ -487,6 +564,25 @@ func TestSemanticStatementConstructClausesLengthCorrectness(t *testing.T) {
?s "old_predicate_3"@[,] ?o3};`,
want: 2,
},
{
query: `deconstruct {?s "predicate_1"@[] ?o1}
in ?a
from ?b
where {?s "old_predicate_1"@[,] ?o1.
?s "old_predicate_2"@[,] ?o2.
?s "old_predicate_3"@[,] ?o3};`,
want: 1,
},
{
query: `deconstruct {?s "predicate_1"@[] ?o1.
?s "predicate_3"@[] ?o3}
in ?a
from ?b
where {?s "old_predicate_1"@[,] ?o1.
?s "old_predicate_2"@[,] ?o2.
?s "old_predicate_3"@[,] ?o3};`,
want: 2,
},
}
p, err := NewParser(SemanticBQL())
if err != nil {
Expand Down Expand Up @@ -578,6 +674,17 @@ func TestSemanticStatementPredicateObjectPairsLengthCorrectness(t *testing.T) {
wantOne: 3,
wantTwo: 3,
},
{
query: `deconstruct {?s "predicate_1"@[] ?o1.
?s1 "predicate_1"@[] ?o1}
in ?a
from ?b
where {?s "old_predicate_1"@[,] ?o1.
?s "old_predicate_2"@[,] ?o2.
?s1 "old_predicate_3"@[,] AT ?t ?o3};`,
wantOne: 1,
wantTwo: 1,
},
}
p, err := NewParser(SemanticBQL())
if err != nil {
Expand Down
18 changes: 18 additions & 0 deletions bql/lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ const (
ItemCreate
// ItemConstruct represents the construct keyword in BQL.
ItemConstruct
// ItemDeconstruct represents the deconstruct keyword in BQL.
ItemDeconstruct
// ItemDrop represent the destruction of a graph in BQL.
ItemDrop
// ItemGraph represent the graph to be created of destroyed in BQL.
Expand All @@ -63,6 +65,8 @@ const (
ItemID
// ItemAt represents at keyword in BQL.
ItemAt
// ItemIn represents in keyword in BQL.
ItemIn
// ItemBefore represents the before keyword in BQL.
ItemBefore
// ItemAfter represents the after keyword in BQL.
Expand Down Expand Up @@ -145,6 +149,8 @@ func (tt TokenType) String() string {
return "CREATE"
case ItemConstruct:
return "CONSTRUCT"
case ItemDeconstruct:
return "DECONSTRUCT"
case ItemDrop:
return "DROP"
case ItemGraph:
Expand Down Expand Up @@ -227,6 +233,8 @@ func (tt TokenType) String() string {
return "TYPE"
case ItemAt:
return "AT"
case ItemIn:
return "IN"
case ItemDistinct:
return "DISTINCT"
default:
Expand Down Expand Up @@ -262,6 +270,7 @@ const (
delete = "delete"
create = "create"
construct = "construct"
deconstruct = "deconstruct"
drop = "drop"
graph = "graph"
data = "data"
Expand All @@ -288,6 +297,7 @@ const (
id = "id"
typeKeyword = "type"
atKeyword = "at"
inKeyword = "in"
anchor = "\"@["
literalType = "\"^^type:"
literalBool = "bool"
Expand Down Expand Up @@ -475,6 +485,10 @@ func lexKeyword(l *lexer) stateFn {
consumeKeyword(l, ItemConstruct)
return lexSpace
}
if strings.EqualFold(input, deconstruct) {
consumeKeyword(l, ItemDeconstruct)
return lexSpace
}
if strings.EqualFold(input, drop) {
consumeKeyword(l, ItemDrop)
return lexSpace
Expand Down Expand Up @@ -579,6 +593,10 @@ func lexKeyword(l *lexer) stateFn {
consumeKeyword(l, ItemAt)
return lexSpace
}
if strings.EqualFold(input, inKeyword) {
consumeKeyword(l, ItemIn)
return lexSpace
}
for {
r := l.next()
if unicode.IsSpace(r) || r == eof {
Expand Down
37 changes: 32 additions & 5 deletions bql/planner/planner.go
Original file line number Diff line number Diff line change
Expand Up @@ -818,18 +818,23 @@ func (p *queryPlan) String() string {
}

// constructPlan encapsulates the sequence of instructions that need to be
// executed in order to satisfy the execution of a valid construct BQL statement.
// executed in order to satisfy the execution of a valid construct or deconstruct
// BQL statement.
type constructPlan struct {
stm *semantic.Statement
store storage.Store
tracer io.Writer
bulkSize int
queryPlan *queryPlan
construct bool
}

// Type returns the type of plan used by the executor.
func (p *constructPlan) Type() string {
return "CONSTRUCT"
if p.construct {
return "CONSTRUCT"
}
return "DECONSTRUCT"
}

func (p *constructPlan) processPredicateObjectPair(pop *semantic.ConstructPredicateObjectPair, tbl *table.Table, r table.Row) (*predicate.Predicate, *triple.Object, error) {
Expand Down Expand Up @@ -926,9 +931,17 @@ func (p *constructPlan) Execute(ctx context.Context) (*table.Table, error) {
var ts []*triple.Triple
updateFunc := func(g storage.Graph, d []*triple.Triple) error {
trace(p.tracer, func() []string {
return []string{"Inserting triples to graph \"" + g.ID(ctx) + "\""}
return []string{"Removing triples from graph \"" + g.ID(ctx) + "\""}
})
return g.AddTriples(ctx, d)
return g.RemoveTriples(ctx, d)
}
if p.construct {
updateFunc = func(g storage.Graph, d []*triple.Triple) error {
trace(p.tracer, func() []string {
return []string{"Inserting triples to graph \"" + g.ID(ctx) + "\""}
})
return g.AddTriples(ctx, d)
}
}
for elem := range tripChan {
ts = append(ts, elem)
Expand Down Expand Up @@ -982,7 +995,10 @@ func (p *constructPlan) Execute(ctx context.Context) (*table.Table, error) {

// String returns a readable description of the execution plan.
func (p *constructPlan) String() string {
b := bytes.NewBufferString("CONSTRUCT plan:\n\n")
b := bytes.NewBufferString("DECONSTRUCT plan:\n\n")
if p.construct {
b = bytes.NewBufferString("CONSTRUCT plan:\n\n")
}
b.WriteString("Input graphs:\n")
for _, gn := range p.stm.InputGraphNames() {
b.WriteString(fmt.Sprintf("\t%v\n", gn))
Expand Down Expand Up @@ -1036,6 +1052,17 @@ func New(ctx context.Context, store storage.Store, stm *semantic.Statement, chan
tracer: w,
bulkSize: bulkSize,
queryPlan: qp,
construct: true,
}, nil
case semantic.Deconstruct:
qp, _ := newQueryPlan(ctx, store, stm, chanSize, w)
return &constructPlan{
stm: stm,
store: store,
tracer: w,
bulkSize: bulkSize,
queryPlan: qp,
construct: false,
}, nil
default:
return nil, fmt.Errorf("planner.New: unknown statement type in statement %v", stm)
Expand Down
Loading