Skip to content

Commit f31b482

Browse files
authored
refactor: Decouple db.db from gql (sourcenetwork#912)
* Replace relationManager call with collection logic Removes odd function from collection/db abi that uses gql schema manager for internal query logic. * Decouple db type from gql parser Decouples the `defra.db` type from the gql.parser, allowing other query languages to (in theory) be swapped in instead. Does not expose this decoupling to users, as that requires futher thought and design. * Remove misuse of mapper types from collection func Function now uses the request model. Keeps things more cleanly seperated, at a small performance cost. * Rename planner constructor * Remove unhelpful func * Remove executor * Move mapper internal to planner Declares it's usage to be internal to the planner package, hopefull discouraging it's usage outside, and hopefully clarifying it's purpose. Is a straight cut-paste, no logic has changed in this commit.
1 parent 7751d40 commit f31b482

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+367
-299
lines changed

client/db.go

-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ type DB interface {
2626
GetCollectionByName(context.Context, string) (Collection, error)
2727
GetCollectionBySchemaID(context.Context, string) (Collection, error)
2828
GetAllCollections(ctx context.Context) ([]Collection, error)
29-
GetRelationshipIdField(fieldName, targetType, thisType string) (string, error)
3029

3130
Root() ds.Batching
3231
Blockstore() blockstore.Blockstore

client/descriptions.go

+12
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ func (col CollectionDescription) GetField(name string) (FieldDescription, bool)
4040
return FieldDescription{}, false
4141
}
4242

43+
// GetRelation returns the field that supports the relation of the given name.
44+
func (col CollectionDescription) GetRelation(name string) (FieldDescription, bool) {
45+
if !col.Schema.IsEmpty() {
46+
for _, field := range col.Schema.Fields {
47+
if field.RelationName == name {
48+
return field, true
49+
}
50+
}
51+
}
52+
return FieldDescription{}, false
53+
}
54+
4355
func (col CollectionDescription) GetPrimaryIndex() IndexDescription {
4456
return col.Indexes[0]
4557
}

core/parser.go

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2022 Democratized Data Foundation
2+
//
3+
// Use of this software is governed by the Business Source License
4+
// included in the file licenses/BSL.txt.
5+
//
6+
// As of the Change Date specified in that file, in accordance with
7+
// the Business Source License, use of this software will be governed
8+
// by the Apache License, Version 2.0, included in the file
9+
// licenses/APL.txt.
10+
11+
package core
12+
13+
import (
14+
"context"
15+
16+
"github.com/sourcenetwork/defradb/client"
17+
"github.com/sourcenetwork/defradb/client/request"
18+
)
19+
20+
type SchemaDefinition struct {
21+
// The name of this schema definition.
22+
Name string
23+
24+
// The serialized definition of this schema definition.
25+
// todo: this needs to be properly typed and handled according to
26+
// https://github.com/sourcenetwork/defradb/issues/863
27+
Body []byte
28+
}
29+
30+
type Schema struct {
31+
// The individual declarations of types defined by this schema.
32+
Definitions []SchemaDefinition
33+
34+
// The collection descriptions created from/defined by this schema.
35+
Descriptions []client.CollectionDescription
36+
}
37+
38+
// Parser represents the object responsible for handling stuff specific to
39+
// a query language. This includes schema and query parsing, and introspection.
40+
type Parser interface {
41+
// Returns true if the given string is an introspection request.
42+
IsIntrospection(request string) bool
43+
44+
// Executes the given introspection request.
45+
ExecuteIntrospection(request string) *client.QueryResult
46+
47+
// Parses the given request, returning a strongly typed model of that request.
48+
Parse(request string) (*request.Request, []error)
49+
50+
// Adds the given schema to this parser's model.
51+
AddSchema(ctx context.Context, schema string) (*Schema, error)
52+
}

db/collection_update.go

+23-41
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"github.com/sourcenetwork/defradb/core"
2424
"github.com/sourcenetwork/defradb/datastore"
2525
"github.com/sourcenetwork/defradb/errors"
26-
"github.com/sourcenetwork/defradb/mapper"
2726
"github.com/sourcenetwork/defradb/planner"
2827
"github.com/sourcenetwork/defradb/query/graphql/parser"
2928
)
@@ -598,81 +597,64 @@ func (c *collection) makeSelectionQuery(
598597
txn datastore.Txn,
599598
filter any,
600599
) (planner.Query, error) {
601-
mapping := c.createMapping()
602-
var f *mapper.Filter
600+
var f client.Option[request.Filter]
603601
var err error
604602
switch fval := filter.(type) {
605603
case string:
606604
if fval == "" {
607605
return nil, errors.New("invalid filter")
608606
}
609-
var p client.Option[request.Filter]
610-
p, err = parser.NewFilterFromString(fval)
607+
608+
f, err = parser.NewFilterFromString(fval)
611609
if err != nil {
612610
return nil, err
613611
}
614-
f = mapper.ToFilter(p, mapping)
615-
case *mapper.Filter:
612+
case client.Option[request.Filter]:
616613
f = fval
617614
default:
618615
return nil, errors.New("invalid filter")
619616
}
620617
if filter == "" {
621618
return nil, errors.New("invalid filter")
622619
}
623-
slct, err := c.makeSelectLocal(f, mapping)
620+
slct, err := c.makeSelectLocal(f)
624621
if err != nil {
625622
return nil, err
626623
}
627624

628-
return c.db.queryExecutor.MakeSelectQuery(ctx, c.db, txn, slct)
625+
planner := planner.New(ctx, c.db, txn)
626+
return planner.MakePlan(&request.Request{
627+
Queries: []*request.OperationDefinition{
628+
{
629+
Selections: []request.Selection{
630+
slct,
631+
},
632+
},
633+
},
634+
})
629635
}
630636

631-
func (c *collection) makeSelectLocal(filter *mapper.Filter, mapping *core.DocumentMapping) (*mapper.Select, error) {
632-
slct := &mapper.Select{
633-
Targetable: mapper.Targetable{
634-
Field: mapper.Field{
635-
Name: c.Name(),
636-
},
637-
Filter: filter,
637+
func (c *collection) makeSelectLocal(filter client.Option[request.Filter]) (*request.Select, error) {
638+
slct := &request.Select{
639+
Field: request.Field{
640+
Name: c.Name(),
638641
},
639-
Fields: make([]mapper.Requestable, len(c.desc.Schema.Fields)),
640-
DocumentMapping: *mapping,
642+
Filter: filter,
643+
Fields: make([]request.Selection, 0),
641644
}
642645

643646
for _, fd := range c.Schema().Fields {
644647
if fd.IsObject() {
645648
continue
646649
}
647-
index := int(fd.ID)
648-
slct.Fields = append(slct.Fields, &mapper.Field{
649-
Index: index,
650-
Name: fd.Name,
650+
slct.Fields = append(slct.Fields, &request.Field{
651+
Name: fd.Name,
651652
})
652653
}
653654

654655
return slct, nil
655656
}
656657

657-
func (c *collection) createMapping() *core.DocumentMapping {
658-
mapping := core.NewDocumentMapping()
659-
mapping.Add(core.DocKeyFieldIndex, request.DocKeyFieldName)
660-
for _, fd := range c.Schema().Fields {
661-
if fd.IsObject() {
662-
continue
663-
}
664-
index := int(fd.ID)
665-
mapping.Add(index, fd.Name)
666-
mapping.RenderKeys = append(mapping.RenderKeys,
667-
core.RenderKey{
668-
Index: index,
669-
Key: fd.Name,
670-
},
671-
)
672-
}
673-
return mapping
674-
}
675-
676658
// getTypeAndCollectionForPatch parses the Patch op path values
677659
// and compares it against the collection schema.
678660
// If it's within the schema, then patchIsSubType is false

db/db.go

+5-32
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ import (
3030
"github.com/sourcenetwork/defradb/events"
3131
"github.com/sourcenetwork/defradb/logging"
3232
"github.com/sourcenetwork/defradb/merkle/crdt"
33-
"github.com/sourcenetwork/defradb/planner"
34-
"github.com/sourcenetwork/defradb/query/graphql/schema"
33+
"github.com/sourcenetwork/defradb/query/graphql"
3534
)
3635

3736
var (
@@ -61,8 +60,7 @@ type db struct {
6160

6261
events client.Events
6362

64-
schema *schema.SchemaManager
65-
queryExecutor *planner.QueryExecutor
63+
parser core.Parser
6664

6765
// The options used to init the database
6866
options any
@@ -92,14 +90,7 @@ func newDB(ctx context.Context, rootstore ds.Batching, options ...Option) (*db,
9290
multistore := datastore.MultiStoreFrom(root)
9391
crdtFactory := crdt.DefaultFactory.WithStores(multistore)
9492

95-
log.Debug(ctx, "Loading: schema manager")
96-
sm, err := schema.NewSchemaManager()
97-
if err != nil {
98-
return nil, err
99-
}
100-
101-
log.Debug(ctx, "Loading: query executor")
102-
exec, err := planner.NewQueryExecutor(sm)
93+
parser, err := graphql.NewParser()
10394
if err != nil {
10495
return nil, err
10596
}
@@ -110,9 +101,8 @@ func newDB(ctx context.Context, rootstore ds.Batching, options ...Option) (*db,
110101

111102
crdtFactory: &crdtFactory,
112103

113-
schema: sm,
114-
queryExecutor: exec,
115-
options: options,
104+
parser: parser,
105+
options: options,
116106
}
117107

118108
// apply options
@@ -190,23 +180,6 @@ func (db *db) PrintDump(ctx context.Context) error {
190180
return printStore(ctx, db.multistore.Rootstore())
191181
}
192182

193-
func (db *db) Executor() *planner.QueryExecutor {
194-
return db.queryExecutor
195-
}
196-
197-
func (db *db) GetRelationshipIdField(fieldName, targetType, thisType string) (string, error) {
198-
rm := db.schema.Relations
199-
rel := rm.GetRelationByDescription(fieldName, targetType, thisType)
200-
if rel == nil {
201-
return "", errors.New("relation does not exists")
202-
}
203-
subtypefieldname, _, ok := rel.GetFieldFromSchemaType(targetType)
204-
if !ok {
205-
return "", errors.New("relation is missing referenced field")
206-
}
207-
return subtypefieldname, nil
208-
}
209-
210183
// Close is called when we are shutting down the database.
211184
// This is the place for any last minute cleanup or releasing of resources (i.e.: Badger instance).
212185
func (db *db) Close(ctx context.Context) {

db/query.go

+29-23
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@ import (
1414
"context"
1515
"strings"
1616

17-
gql "github.com/graphql-go/graphql"
18-
1917
"github.com/sourcenetwork/defradb/client"
2018
"github.com/sourcenetwork/defradb/datastore"
19+
"github.com/sourcenetwork/defradb/planner"
2120
)
2221

2322
func (db *db) ExecQuery(ctx context.Context, query string) *client.QueryResult {
@@ -34,7 +33,18 @@ func (db *db) ExecQuery(ctx context.Context, query string) *client.QueryResult {
3433
}
3534
defer txn.Discard(ctx)
3635

37-
results, err := db.queryExecutor.ExecQuery(ctx, db, txn, query)
36+
request, errors := db.parser.Parse(query)
37+
if len(errors) > 0 {
38+
errorStrings := make([]any, len(errors))
39+
for i, err := range errors {
40+
errorStrings[i] = err.Error()
41+
}
42+
res.Errors = errorStrings
43+
return res
44+
}
45+
46+
planner := planner.New(ctx, db, txn)
47+
results, err := planner.RunRequest(ctx, request)
3848
if err != nil {
3949
res.Errors = []any{err.Error()}
4050
return res
@@ -54,13 +64,24 @@ func (db *db) ExecTransactionalQuery(
5464
query string,
5565
txn datastore.Txn,
5666
) *client.QueryResult {
57-
res := &client.QueryResult{}
58-
// check if its Introspection query
59-
if strings.Contains(query, "IntrospectionQuery") {
67+
if db.parser.IsIntrospection(query) {
6068
return db.ExecIntrospection(query)
6169
}
6270

63-
results, err := db.queryExecutor.ExecQuery(ctx, db, txn, query)
71+
res := &client.QueryResult{}
72+
73+
request, errors := db.parser.Parse(query)
74+
if len(errors) > 0 {
75+
errorStrings := make([]any, len(errors))
76+
for i, err := range errors {
77+
errorStrings[i] = err.Error()
78+
}
79+
res.Errors = errorStrings
80+
return res
81+
}
82+
83+
planner := planner.New(ctx, db, txn)
84+
results, err := planner.RunRequest(ctx, request)
6485
if err != nil {
6586
res.Errors = []any{err.Error()}
6687
return res
@@ -71,20 +92,5 @@ func (db *db) ExecTransactionalQuery(
7192
}
7293

7394
func (db *db) ExecIntrospection(query string) *client.QueryResult {
74-
schema := db.schema.Schema()
75-
// t := schema.Type("userFilterArg")
76-
// spew.Dump(t.(*gql.InputObject).Fields())
77-
params := gql.Params{Schema: *schema, RequestString: query}
78-
r := gql.Do(params)
79-
80-
res := &client.QueryResult{
81-
Data: r.Data,
82-
Errors: make([]any, len(r.Errors)),
83-
}
84-
85-
for i, err := range r.Errors {
86-
res.Errors[i] = err
87-
}
88-
89-
return res
95+
return db.parser.ExecuteIntrospection(query)
9096
}

0 commit comments

Comments
 (0)