diff --git a/planner/commit.go b/planner/commit.go index 7b95eb978b..286d4ecb32 100644 --- a/planner/commit.go +++ b/planner/commit.go @@ -400,5 +400,3 @@ func (n *dagScanNode) dagBlockToNodeDoc(block blocks.Block) (core.Doc, []*ipld.L return commit, heads, nil } - -func (n *dagScanNode) Append() bool { return true } diff --git a/planner/multi.go b/planner/multi.go index 30bbc8338c..1b5fc14bbc 100644 --- a/planner/multi.go +++ b/planner/multi.go @@ -32,23 +32,6 @@ type MultiNode interface { Children() []planNode } -// mergeNode is a special interface for the MultiNode -// system. A mergeNode provides an entire document -// in its Values() func, with all the specific and -// necessary fields and subfields already merged -// into the doc -type mergeNode interface { - planNode - Merge() bool -} - -// appendNode is a special interface for the MultiNode -// system. -type appendNode interface { - planNode - Append() bool -} - // parallelNode implements the MultiNode interface. It // enables parallel execution of planNodes. This is needed // if a single request has multiple Select statements at the @@ -132,10 +115,10 @@ func (p *parallelNode) Next() (bool, error) { var err error // isMerge := false switch n := plan.(type) { - case mergeNode: + case *scanNode, *typeIndexJoin: // isMerge = true next, err = p.nextMerge(i, n) - case appendNode: + case *dagScanNode: next, err = p.nextAppend(i, n) } if err != nil { @@ -148,7 +131,7 @@ func (p *parallelNode) Next() (bool, error) { return orNext, nil } -func (p *parallelNode) nextMerge(index int, plan mergeNode) (bool, error) { +func (p *parallelNode) nextMerge(index int, plan planNode) (bool, error) { if next, err := plan.Next(); !next { return false, err } @@ -159,52 +142,7 @@ func (p *parallelNode) nextMerge(index int, plan mergeNode) (bool, error) { return true, nil } -/* - -scan node -========= -{ - _docID: bae-ALICE, - name: Alice, - points: 124, - verified: false -} - -typeJoin node(merge) -============= -{ - friends: [ - { - _docID: bae-BOB, - name: bob, - points: 99.9, - verified: true, - } - ] -} - -output -====== - -{ - _docID: bae-ALICE, - name: Alice, - points: 124, - verified: false, - - friends: [ - { - _docID: bae-BOB, - name: bob, - points: 99.9, - verified: true, - } - ] -} - -*/ - -func (p *parallelNode) nextAppend(index int, plan appendNode) (bool, error) { +func (p *parallelNode) nextAppend(index int, plan planNode) (bool, error) { key := p.currentValue.GetID() if key == "" { return false, nil @@ -235,43 +173,6 @@ func (p *parallelNode) nextAppend(index int, plan appendNode) (bool, error) { return true, nil } -/* - -query { - user { - _docID - name - points - verified - - _version { - cid - } - } -} - -scan node -========= -{ - _docID: bae-ALICE, - name: Alice, - points: 124, - verified: false -} - -_version: commitSelectTopNode(append) -=================== -[ - { - cid: QmABC - }, - { - cid: QmDEF - } - ... -] -*/ - func (p *parallelNode) Source() planNode { return p.multiscan } func (p *parallelNode) Children() []planNode { @@ -283,171 +184,66 @@ func (p *parallelNode) addChild(fieldIndex int, node planNode) { p.childIndexes = append(p.childIndexes, fieldIndex) } -/* -user { - friends { - name - } - - addresses { - street_name - } -} - -Select { - source: scanNode(user) -} - - |||||| - \/\/\/ - -Select { - source: TypeJoin(friends, user) { - joinPlan { - typeJoinMany { - root: scanNode(user) - subType: Select { - source: scanNode(friends) - } - } - } - }, -} - - |||||| - \/\/\/ - -Select { - source: MultiNode[ - { - TypeJoin(friends, user) { - joinPlan { - typeJoinMany { - root: multiscan(scanNode(user)) - subType: Select { - source: scanNode(friends) - } - } - } - } - }, - { - TypeJoin(addresses, user) { - joinPlan { - typeJoinMany { - root: multiscan(scanNode(user)) - subType: Select { - source: scanNode(addresses) - } - } - } - } - }] - } -} - -select addSubPlan { - check if source is MultiNode - yes => - get multiScan node - create new plan with multi scan node - append - no = > - create new multinode - get scan node from existing source - create multiscan - replace existing source scannode with multiScan - add existing source to new MultiNode - add new plan to multNode - -} - -Select { - source: Parallel {[ - TypeJoin { - - }, - commitScan { - - } - ]} -} -*/ - -// @todo: Document AddSubPlan method -func (s *selectNode) addSubPlan(fieldIndex int, plan planNode) error { - src := s.source - switch node := src.(type) { +func (s *selectNode) addSubPlan(fieldIndex int, newPlan planNode) error { + switch sourceNode := s.source.(type) { // if its a scan node, we either replace or create a multinode case *scanNode, *pipeNode: - switch plan.(type) { - case mergeNode: - s.source = plan - case appendNode: + switch newPlan.(type) { + case *scanNode, *typeIndexJoin: + s.source = newPlan + case *dagScanNode: m := ¶llelNode{ p: s.planner, - docMapper: docMapper{src.DocumentMap()}, + docMapper: docMapper{s.source.DocumentMap()}, } - m.addChild(-1, src) - m.addChild(fieldIndex, plan) + m.addChild(-1, s.source) + m.addChild(fieldIndex, newPlan) s.source = m default: - return client.NewErrUnhandledType("sub plan", plan) + return client.NewErrUnhandledType("sub plan", newPlan) } - // source is a mergeNode, like a TypeJoin - case mergeNode: - origScan, _ := walkAndFindPlanType[*scanNode](plan) + case *typeIndexJoin: + origScan, _ := walkAndFindPlanType[*scanNode](newPlan) if origScan == nil { return ErrFailedToFindScanNode } // create our new multiscanner multiscan := &multiScanNode{scanNode: origScan} // replace our current source internal scanNode with our new multiscanner - if err := s.planner.walkAndReplacePlan(src, origScan, multiscan); err != nil { + if err := s.planner.walkAndReplacePlan(s.source, origScan, multiscan); err != nil { return err } // create multinode multinode := ¶llelNode{ p: s.planner, multiscan: multiscan, - docMapper: docMapper{src.DocumentMap()}, + docMapper: docMapper{s.source.DocumentMap()}, } - multinode.addChild(-1, src) + multinode.addChild(-1, s.source) multiscan.addReader() // replace our new node internal scanNode with our new multiscanner - if err := s.planner.walkAndReplacePlan(plan, origScan, multiscan); err != nil { + if err := s.planner.walkAndReplacePlan(newPlan, origScan, multiscan); err != nil { return err } // add our newly updated plan to the multinode - multinode.addChild(fieldIndex, plan) + multinode.addChild(fieldIndex, newPlan) multiscan.addReader() s.source = multinode // we already have an existing parallelNode as our source case *parallelNode: - switch plan.(type) { - // easy, just append, since append doest need any internal relaced scannode - case appendNode: - node.addChild(fieldIndex, plan) - + switch newPlan.(type) { // We have a internal multiscanNode on our MultiNode - case mergeNode: - multiscan, sourceIsMultiscan := node.Source().(*multiScanNode) - if !sourceIsMultiscan { - return client.NewErrUnexpectedType[*multiScanNode]("mergeNode", node.Source()) - } - + case *scanNode, *typeIndexJoin: // replace our new node internal scanNode with our existing multiscanner - if err := s.planner.walkAndReplacePlan(plan, multiscan.Source(), multiscan); err != nil { + if err := s.planner.walkAndReplacePlan(newPlan, sourceNode.multiscan.Source(), sourceNode.multiscan); err != nil { return err } - multiscan.addReader() - // add our newly updated plan to the multinode - node.addChild(fieldIndex, plan) - default: - return client.NewErrUnhandledType("sub plan", plan) + sourceNode.multiscan.addReader() } + + sourceNode.addChild(fieldIndex, newPlan) } return nil } diff --git a/planner/scan.go b/planner/scan.go index a11f18de5e..5ea81c8f69 100644 --- a/planner/scan.go +++ b/planner/scan.go @@ -288,9 +288,6 @@ func (n *scanNode) Explain(explainType request.ExplainType) (map[string]any, err } } -// Merge implements mergeNode -func (n *scanNode) Merge() bool { return true } - func (p *Planner) Scan( mapperSelect *mapper.Select, colDesc client.CollectionDescription, diff --git a/planner/type_join.go b/planner/type_join.go index a39af2fae7..3161858f77 100644 --- a/planner/type_join.go +++ b/planner/type_join.go @@ -220,9 +220,6 @@ func (n *typeIndexJoin) Explain(explainType request.ExplainType) (map[string]any } } -// Merge implements mergeNode -func (n *typeIndexJoin) Merge() bool { return true } - // typeJoinOne is the plan node for a type index join // where the root type is the primary in a one-to-one relation request. type typeJoinOne struct { diff --git a/tests/integration/query/one_to_one/with_version_test.go b/tests/integration/query/one_to_one/with_version_test.go new file mode 100644 index 0000000000..4c3656a312 --- /dev/null +++ b/tests/integration/query/one_to_one/with_version_test.go @@ -0,0 +1,83 @@ +// Copyright 2024 Democratized Data Foundation +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package one_to_one + +// This test documents unwanted behaviour, see the linked ticket for more info: +// https://github.com/sourcenetwork/defradb/issues/1709 +// +// It is currently commented out because the panic is caught in the CLI and HTTP clients +// and we have no good way atm to skip it. +/*func TestQueryOneToOne_WithVersionOnOuter(t *testing.T) { + test := testUtils.TestCase{ + Description: "Embedded commits query within one-one query", + Actions: []any{ + testUtils.SchemaUpdate{ + Schema: ` + type Book { + name: String + author: Author + } + + type Author { + name: String + published: Book @primary + } + `, + }, + testUtils.CreateDoc{ + CollectionID: 0, + Doc: `{ + "name": "فارسی دوم دبستان" + }`, + }, + testUtils.CreateDoc{ + CollectionID: 1, + Doc: `{ + "name": "نمی دانم", + "published": "bae-c052eade-23f6-5ee3-8067-20004e746be3" + }`, + }, + testUtils.Request{ + Request: ` + query { + Book { + name + _version { + docID + } + author { + name + } + } + } + `, + Results: []map[string]any{ + { + "name": "نمی دانم", + "_version": []map[string]any{ + { + "docID": "bae-c052eade-23f6-5ee3-8067-20004e746be3", + }, + }, + "author": map[string]any{ + "name": "فارسی دوم دبستان", + }, + }, + }, + }, + }, + } + + require.Panics(t, + func() { testUtils.ExecuteTestCase(t, test) }, + ) +} +*/