diff --git a/client/db.go b/client/db.go index b8742f7d3f..b99f0c8b88 100644 --- a/client/db.go +++ b/client/db.go @@ -19,30 +19,75 @@ import ( "github.com/sourcenetwork/defradb/events" ) +// DB is the primary public programmatic access point to the local DefraDB instance. +// +// It should be constructed via the [db] package, via the [db.NewDB] function. type DB interface { + // Store contains DefraDB functions protected by an internal, short-lived, transaction, allowing safe + // access to common database read and write operations. Store + // NewTxn returns a new transaction on the root store that may be managed externally. + // + // It may be used with other functions in the client package. It is not threadsafe. NewTxn(context.Context, bool) (datastore.Txn, error) + + // NewConcurrentTxn returns a new transaction on the root store that may be managed externally. + // + // It may be used with other functions in the client package. It is threadsafe and mutliple threads/Go routines + // can safely operate on it concurrently. NewConcurrentTxn(context.Context, bool) (datastore.Txn, error) + // WithTxn returns a new [client.Store] that respects the given transaction. WithTxn(datastore.Txn) Store + // Root returns the underlying root store, within which all data managed by DefraDB is held. Root() datastore.RootStore + + // Blockstore returns the blockstore, within which all blocks (commits) managed by DefraDB are held. + // + // It sits within the rootstore returned by [Root]. Blockstore() blockstore.Blockstore + // Close closes the database instance and releases any resources held. + // + // The behaviour of other functions in this package after this function has been called is undefined + // unless explicitly stated on the function in question. + // + // It does not explicitly clear any data from persisted storage, and a new [DB] instance may typically + // be created after calling this to resume operations on the prior data - this is however dependant on + // the behaviour of the rootstore provided on database instance creation, as this function will Close + // the provided rootstore. Close(context.Context) + // Events returns the database event queue. + // + // It may be used to monitor database events - a new event will be yielded for each mutation. + // Note: it does not copy the queue, just the reference to it. Events() events.Events + // MaxTxnRetries returns the number of retries that this DefraDB instance has been configured to + // make in the event of a transaction conflict in certain scenarios. + // + // Currently this is only used within the P2P system and will not affect operations initiated by users. MaxTxnRetries() int + // PrintDump logs the entire contents of the rootstore (all the data managed by this DefraDB instance). + // + // It is likely unwise to call this on a large database instance. PrintDump(ctx context.Context) error } +// Store contains the core DefraDB read-write operations. type Store interface { // P2P holds the P2P related methods that must be implemented by the database. P2P + // AddSchema takes the provided GQL schema in SDL format, and applies it to the [Store], + // creating the necessary collections, request types, etc. + // + // All schema types provided must not exist prior to calling this, and they may not reference existing + // types previously defined. AddSchema(context.Context, string) error // PatchSchema takes the given JSON patch string and applies it to the set of CollectionDescriptions @@ -61,6 +106,10 @@ type Store interface { // [FieldKindStringToEnumMapping]. PatchSchema(context.Context, string) error + // CreateCollection creates a new collection using the given description. + // + // WARNING: It does not currently update the GQL types, and as such a database restart is required after + // calling this if use of the new collection via GQL is desired (for example via [ExecRequest]). CreateCollection(context.Context, CollectionDescription) (Collection, error) // UpdateCollection updates the persisted collection description matching the name of the given @@ -71,6 +120,9 @@ type Store interface { // The collection (including the schema version ID) will only be updated if any changes have actually // been made, if the given description matches the current persisted description then no changes will be // applied. + // + // WARNING: It does not currently update the GQL types, and as such a database restart is required after + // calling this if use of the new collection via GQL is desired (for example via [ExecRequest]). UpdateCollection(context.Context, CollectionDescription) (Collection, error) // ValidateUpdateCollection validates that the given collection description is a valid update. @@ -79,20 +131,51 @@ type Store interface { // collection. Will return an error if it fails validation. ValidateUpdateCollection(context.Context, CollectionDescription) (bool, error) + // GetCollectionByName attempts to retrieve a collection matching the given name. + // + // If no matching collection is found an error will be returned. GetCollectionByName(context.Context, string) (Collection, error) + + // GetCollectionBySchemaID attempts to retrieve a collection matching the given schema ID. + // + // If no matching collection is found an error will be returned. GetCollectionBySchemaID(context.Context, string) (Collection, error) + + // GetCollectionBySchemaID attempts to retrieve a collection matching the given schema version ID. + // + // If no matching collection is found an error will be returned. GetCollectionByVersionID(context.Context, string) (Collection, error) + + // GetAllCollections returns all the collections and their descriptions that currently exist within + // this [Store]. GetAllCollections(context.Context) ([]Collection, error) + // ExecRequest executes the given GQL request against the [Store]. ExecRequest(context.Context, string) *RequestResult } +// GQLResult represents the immediate results of a GQL request. +// +// It does not handle subscription channels. This object and its children are json serializable. type GQLResult struct { + // Errors contains any errors generated whilst attempting to execute the request. + // + // If there are values in this slice the request will likely not have run to completion + // and [Data] will be nil. Errors []any `json:"errors,omitempty"` - Data any `json:"data"` + + // Data contains the resultant data produced by the GQL request. + // + // It will be nil if any errors were raised during execution. + Data any `json:"data"` } +// RequestResult represents the results of a GQL request. type RequestResult struct { + // GQL contains the immediate results of the GQL request. GQL GQLResult + + // Pub contains a pointer to an event stream which channels any subscription results + // if the request was a GQL subscription. Pub *events.Publisher[events.Update] } diff --git a/client/value.go b/client/value.go index 4b916e8382..c07f265957 100644 --- a/client/value.go +++ b/client/value.go @@ -109,44 +109,3 @@ func newCBORValue(t CType, val any) WriteableValue { func (v cborValue) Bytes() ([]byte, error) { return cbor.Marshal(v.value) } - -// func ReadCBORValue() - -// func (val simpleValue) GetCRDT() crdt.MerkleCRDT { -// return val.crdt -// } - -// func (val *simpleValue) SetCRDT(crdt crdt.MerkleCRDT) error { -// // if val.Type() != client.CType() { - -// // } else { - -// // } -// val.crdt = crdt -// return nil -// } - -// type merkleCRDTValue struct { -// crdt crdt.MerkleCRDT -// } - -// func newMerkleCRDTValue(dt crdt.MerkleCRDT) *merkleCRDTValue { -// return &merkleCRDTValue{ -// crdt: dt -// } -// } - -// type listValue struct { -// vals []Value -// } - -// func (l *listValue) Value() any { -// return l.vals -// } -// func (l *listValue) IsDocument() bool { return false } -// func (l *listValue) Type() client.CType { } -// func (l *listValue) IsDirty() bool -// func (l *listValue) Clean() -// func (l *listValue) IsDelete() bool //todo: Update IsDelete naming -// func (l *listValue) Delete() -// func (l *listValue) Append(v Value) diff --git a/db/txn_db.go b/db/txn_db.go index 7ebc24c323..ac4be22e40 100644 --- a/db/txn_db.go +++ b/db/txn_db.go @@ -209,8 +209,11 @@ func (db *explicitTxnDB) GetAllCollections(ctx context.Context) ([]client.Collec return db.getAllCollections(ctx, db.txn) } -// AddSchema takes the provided schema in SDL format, and applies it to the database, -// and creates the necessary collections, request types, etc. +// AddSchema takes the provided GQL schema in SDL format, and applies it to the database, +// creating the necessary collections, request types, etc. +// +// All schema types provided must not exist prior to calling this, and they may not reference existing +// types previously defined. func (db *implicitTxnDB) AddSchema(ctx context.Context, schemaString string) error { txn, err := db.NewTxn(ctx, false) if err != nil { @@ -226,8 +229,11 @@ func (db *implicitTxnDB) AddSchema(ctx context.Context, schemaString string) err return txn.Commit(ctx) } -// AddSchema takes the provided schema in SDL format, and applies it to the database, -// and creates the necessary collections, request types, etc. +// AddSchema takes the provided GQL schema in SDL format, and applies it to the database, +// creating the necessary collections, request types, etc. +// +// All schema types provided must not exist prior to calling this, and they may not reference existing +// types previously defined. func (db *explicitTxnDB) AddSchema(ctx context.Context, schemaString string) error { return db.addSchema(ctx, db.txn, schemaString) }