...

Source file src/github.com/go-kivik/kivik/v4/driver/driver.go

Documentation: github.com/go-kivik/kivik/v4/driver

     1  // Licensed under the Apache License, Version 2.0 (the "License"); you may not
     2  // use this file except in compliance with the License. You may obtain a copy of
     3  // the License at
     4  //
     5  //  http://www.apache.org/licenses/LICENSE-2.0
     6  //
     7  // Unless required by applicable law or agreed to in writing, software
     8  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     9  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    10  // License for the specific language governing permissions and limitations under
    11  // the License.
    12  
    13  package driver
    14  
    15  import (
    16  	"context"
    17  	"encoding/json"
    18  	"io"
    19  	"time"
    20  )
    21  
    22  // Options represents a collection of arbitrary client or query options.
    23  //
    24  // An implementation should also implement the [fmt.Stringer] interface for the
    25  // sake of display when used by [github.com/go-kivik/kivik/v4/mockdb].
    26  type Options interface {
    27  	// Apply applies the option to target, if target is of the expected type.
    28  	// Unexpected/recognized target types should be ignored.
    29  	Apply(target interface{})
    30  }
    31  
    32  // Driver is the interface that must be implemented by a database driver.
    33  type Driver interface {
    34  	// NewClient returns a connection handle to the database. The name is in a
    35  	// driver-specific format.
    36  	NewClient(name string, options Options) (Client, error)
    37  }
    38  
    39  // Version represents a server version response.
    40  type Version struct {
    41  	// Version is the version number reported by the server or backend.
    42  	Version string
    43  	// Vendor is the vendor string reported by the server or backend.
    44  	Vendor string
    45  	// Features is a list of enabled, optional features.  This was added in
    46  	// CouchDB 2.1.0, and can be expected to be empty for older versions.
    47  	Features []string
    48  	// RawResponse is the raw response body as returned by the server.
    49  	RawResponse json.RawMessage
    50  }
    51  
    52  // Client is a connection to a database server.
    53  type Client interface {
    54  	// Version returns the server implementation's details.
    55  	Version(ctx context.Context) (*Version, error)
    56  	// AllDBs returns a list of all existing database names.
    57  	AllDBs(ctx context.Context, options Options) ([]string, error)
    58  	// DBExists returns true if the database exists.
    59  	DBExists(ctx context.Context, dbName string, options Options) (bool, error)
    60  	// CreateDB creates the requested database.
    61  	CreateDB(ctx context.Context, dbName string, options Options) error
    62  	// DestroyDB deletes the requested database.
    63  	DestroyDB(ctx context.Context, dbName string, options Options) error
    64  	// DB returns a handle to the requested database.
    65  	DB(dbName string, options Options) (DB, error)
    66  }
    67  
    68  // DBsStatser is an optional interface that a [DB] may implement, added to
    69  // support CouchDB 2.2.0's /_dbs_info endpoint. If this is not supported, or
    70  // if this method returns status 404, Kivik will fall back to calling the method
    71  // of issuing a GET /{db} for each database requested.
    72  type DBsStatser interface {
    73  	// DBsStats returns database statistical information for each database
    74  	// named in dbNames. The returned values should be in the same order as
    75  	// the requested database names, and any missing databases should return
    76  	// a nil *DBStats value.
    77  	DBsStats(ctx context.Context, dbNames []string) ([]*DBStats, error)
    78  }
    79  
    80  // AllDBsStatser is an optional interface that a [DB] may implement, added to
    81  // support CouchDB 3.2's GET /_dbs_info endpoint. If this is not supported, or
    82  // if this method returns status 404 or 405, Kivik will fall back to using
    83  // _all_dbs + the [DBStatser] interface (or its respective emulation).
    84  type AllDBsStatser interface {
    85  	// AllDBsStats returns database statistical information for each database
    86  	// in the CouchDB instance. See the [CouchDB documentation] for supported
    87  	// options.
    88  	//
    89  	// [CouchDB documentation]: https://docs.couchdb.org/en/stable/api/server/common.html#get--_dbs_info
    90  	AllDBsStats(ctx context.Context, options Options) ([]*DBStats, error)
    91  }
    92  
    93  // Replication represents a _replicator document.
    94  type Replication interface {
    95  	// The following methods are called just once, when the Replication is first
    96  	// returned from [ClientReplicator.Replicate] or
    97  	// [ClientReplicator.GetReplications].
    98  	ReplicationID() string
    99  	Source() string
   100  	Target() string
   101  
   102  	// The following methods return values may be updated by calls to [Update].
   103  	StartTime() time.Time
   104  	EndTime() time.Time
   105  	State() string
   106  	Err() error
   107  
   108  	// These methods may be triggered by user actions.
   109  
   110  	// Delete deletes a replication, which cancels it if it is running.
   111  	Delete(context.Context) error
   112  	// Update fetches the latest replication state from the server.
   113  	Update(context.Context, *ReplicationInfo) error
   114  }
   115  
   116  // ReplicationInfo represents a snapshot state of a replication, as provided
   117  // by the _active_tasks endpoint.
   118  type ReplicationInfo struct {
   119  	DocWriteFailures int64
   120  	DocsRead         int64
   121  	DocsWritten      int64
   122  	Progress         float64
   123  }
   124  
   125  // ClientReplicator is an optional interface that may be implemented by a [Client]
   126  // that supports replication between two database.
   127  type ClientReplicator interface {
   128  	// Replicate initiates a replication.
   129  	Replicate(ctx context.Context, targetDSN, sourceDSN string, options Options) (Replication, error)
   130  	// GetReplications returns a list of replications (i.e. all docs in the
   131  	// _replicator database)
   132  	GetReplications(ctx context.Context, options Options) ([]Replication, error)
   133  }
   134  
   135  // DBStats contains database statistics.
   136  type DBStats struct {
   137  	Name           string          `json:"db_name"`
   138  	CompactRunning bool            `json:"compact_running"`
   139  	DocCount       int64           `json:"doc_count"`
   140  	DeletedCount   int64           `json:"doc_del_count"`
   141  	UpdateSeq      string          `json:"update_seq"`
   142  	DiskSize       int64           `json:"disk_size"`
   143  	ActiveSize     int64           `json:"data_size"`
   144  	ExternalSize   int64           `json:"-"`
   145  	Cluster        *ClusterStats   `json:"cluster,omitempty"`
   146  	RawResponse    json.RawMessage `json:"-"`
   147  }
   148  
   149  // ClusterStats contains the cluster configuration for the database.
   150  type ClusterStats struct {
   151  	Replicas    int `json:"n"`
   152  	Shards      int `json:"q"`
   153  	ReadQuorum  int `json:"r"`
   154  	WriteQuorum int `json:"w"`
   155  }
   156  
   157  // Members represents the members of a database security document.
   158  type Members struct {
   159  	Names []string `json:"names,omitempty"`
   160  	Roles []string `json:"roles,omitempty"`
   161  }
   162  
   163  // Security represents a database security document.
   164  type Security struct {
   165  	Admins          Members             `json:"admins,omitempty"`
   166  	Members         Members             `json:"members,omitempty"`
   167  	Cloudant        map[string][]string `json:"cloudant,omitempty"`
   168  	CouchdbAuthOnly *bool               `json:"couchdb_auth_only,omitempty"`
   169  }
   170  
   171  // MarshalJSON satisfies the json.Marshaler interface.
   172  func (s Security) MarshalJSON() ([]byte, error) {
   173  	var v struct {
   174  		Admins          *Members            `json:"admins,omitempty"`
   175  		Members         *Members            `json:"members,omitempty"`
   176  		Cloudant        map[string][]string `json:"cloudant,omitempty"`
   177  		CouchdbAuthOnly *bool               `json:"couchdb_auth_only,omitempty"`
   178  	}
   179  	if len(s.Admins.Names) > 0 || len(s.Admins.Roles) > 0 {
   180  		v.Admins = &s.Admins
   181  	}
   182  	if len(s.Members.Names) > 0 || len(s.Members.Roles) > 0 {
   183  		v.Members = &s.Members
   184  	}
   185  	if len(s.Cloudant) > 0 {
   186  		v.Cloudant = s.Cloudant
   187  	}
   188  	if s.CouchdbAuthOnly != nil {
   189  		v.CouchdbAuthOnly = s.CouchdbAuthOnly
   190  	}
   191  	return json.Marshal(v)
   192  }
   193  
   194  // DB is a database handle.
   195  type DB interface {
   196  	// AllDocs returns all of the documents in the database, subject to the
   197  	// options provided.
   198  	AllDocs(ctx context.Context, options Options) (Rows, error)
   199  	// Put writes the document in the database.
   200  	Put(ctx context.Context, docID string, doc interface{}, options Options) (rev string, err error)
   201  	// Get fetches the requested document from the database.
   202  	Get(ctx context.Context, docID string, options Options) (*Document, error)
   203  	// Delete marks the specified document as deleted.
   204  	Delete(ctx context.Context, docID string, options Options) (newRev string, err error)
   205  	// Stats returns database statistics.
   206  	Stats(ctx context.Context) (*DBStats, error)
   207  	// Compact initiates compaction of the database.
   208  	Compact(ctx context.Context) error
   209  	// CompactView initiates compaction of the view.
   210  	CompactView(ctx context.Context, ddocID string) error
   211  	// ViewCleanup cleans up stale view files.
   212  	ViewCleanup(ctx context.Context) error
   213  	// Changes returns an iterator for the changes feed. In continuous mode,
   214  	// the iterator will continue indefinitely, until [Changes.Close] is called.
   215  	Changes(ctx context.Context, options Options) (Changes, error)
   216  	// PutAttachment uploads an attachment to the specified document, returning
   217  	// the new revision.
   218  	PutAttachment(ctx context.Context, docID string, att *Attachment, options Options) (newRev string, err error)
   219  	// GetAttachment fetches an attachment for the associated document ID.
   220  	GetAttachment(ctx context.Context, docID, filename string, options Options) (*Attachment, error)
   221  	// DeleteAttachment deletes an attachment from a document, returning the
   222  	// document's new revision.
   223  	DeleteAttachment(ctx context.Context, docID, filename string, options Options) (newRev string, err error)
   224  	// Query performs a query against a view, subject to the options provided.
   225  	// ddoc will be the design doc name without the '_design/' previx.
   226  	// view will be the view name without the '_view/' prefix.
   227  	Query(ctx context.Context, ddoc, view string, options Options) (Rows, error)
   228  	// Close is called to clean up any resources used by the database.
   229  	Close() error
   230  }
   231  
   232  // DocCreator is an optional interface that extends a [DB] to support the
   233  // creation of new documents. If not implemented, [DB.Put] will be used to
   234  // emulate the functionality, with missing document IDs generated as V4 UUIDs.
   235  type DocCreator interface {
   236  	// CreateDoc creates a new doc, with a server-generated ID.
   237  	CreateDoc(ctx context.Context, doc interface{}, options Options) (docID, rev string, err error)
   238  }
   239  
   240  // OpenRever is an optional interface that extends a [DB] to support the open_revs
   241  // option of the CouchDB get document endpoint. It is used by the replicator.
   242  // Drivers that don't support this endpoint may not be able to replicate as
   243  // efficiently, or at all.
   244  type OpenRever interface {
   245  	// OpenRevs fetches the requested document revisions from the database.
   246  	// revs may be a list of revisions, or a single item with value "all" to
   247  	// request all open revs.
   248  	OpenRevs(ctx context.Context, docID string, revs []string, options Options) (Rows, error)
   249  }
   250  
   251  // SecurityDB is an optional interface that extends a [DB], for backends which
   252  // support security documents.
   253  type SecurityDB interface {
   254  	// Security returns the database's security document.
   255  	Security(ctx context.Context) (*Security, error)
   256  	// SetSecurity sets the database's security document.
   257  	SetSecurity(ctx context.Context, security *Security) error
   258  }
   259  
   260  // Document represents a single document returned by [DB.Get].
   261  type Document struct {
   262  	// Rev is the revision number returned
   263  	Rev string
   264  
   265  	// Body returns the document JSON.
   266  	Body io.ReadCloser
   267  
   268  	// Attachments will be nil except when attachments=true.
   269  	Attachments Attachments
   270  }
   271  
   272  // Attachments is an iterator over the attachments included in a document when
   273  // [DB.Get] is called with `attachments=true`.
   274  type Attachments interface {
   275  	// Next is called to populate att with the next attachment in the result
   276  	// set.
   277  	//
   278  	// Next should return [io.EOF] when there are no more attachments.
   279  	Next(att *Attachment) error
   280  
   281  	// Close closes the Attachments iterator.
   282  	Close() error
   283  }
   284  
   285  // Purger is an optional interface which may be implemented by a [DB] to support
   286  // document purging.
   287  type Purger interface {
   288  	// Purge permanently removes the references to deleted documents from the
   289  	// database.
   290  	Purge(ctx context.Context, docRevMap map[string][]string) (*PurgeResult, error)
   291  }
   292  
   293  // PurgeResult is the result of a purge request.
   294  type PurgeResult struct {
   295  	Seq    int64               `json:"purge_seq"`
   296  	Purged map[string][]string `json:"purged"`
   297  }
   298  
   299  // BulkDocer is an optional interface which may be implemented by a [DB] to
   300  // support bulk insert/update operations. For any driver that does not support
   301  // the BulkDocer interface, the [DB.Put] or [DB.CreateDoc] methods will be
   302  // called for each document to emulate the same functionality, with options
   303  // passed through unaltered.
   304  type BulkDocer interface {
   305  	// BulkDocs alls bulk create, update and/or delete operations. It returns an
   306  	// iterator over the results.
   307  	BulkDocs(ctx context.Context, docs []interface{}, options Options) ([]BulkResult, error)
   308  }
   309  
   310  // Finder is an optional interface which may be implemented by a [DB]. It
   311  // provides access to the MongoDB-style query interface added in CouchDB 2.
   312  type Finder interface {
   313  	// Find executes a query using the new /_find interface. query is always
   314  	// converted to a [encoding/json.RawMessage] value before passing it to the
   315  	// driver. The type remains `interface{}` for backward compatibility, but
   316  	// will change with Kivik 5.x. See [issue #1015] for details.
   317  	//
   318  	// [issue #1014]: https://github.com/go-kivik/kivik/issues/1015
   319  	Find(ctx context.Context, query interface{}, options Options) (Rows, error)
   320  	// CreateIndex creates an index if it doesn't already exist. If the index
   321  	// already exists, it should do nothing. ddoc and name may be empty, in
   322  	// which case they should be provided by the backend. If index is a string,
   323  	// []byte, or [encoding/json.RawMessage], it should be treated as a raw
   324  	// JSON payload. Any other type should be marshaled to JSON.
   325  	CreateIndex(ctx context.Context, ddoc, name string, index interface{}, options Options) error
   326  	// GetIndexes returns a list of all indexes in the database.
   327  	GetIndexes(ctx context.Context, options Options) ([]Index, error)
   328  	// Delete deletes the requested index.
   329  	DeleteIndex(ctx context.Context, ddoc, name string, options Options) error
   330  	// Explain returns the query plan for a given query. Explain takes the same
   331  	// arguments as [Finder.Find].
   332  	Explain(ctx context.Context, query interface{}, options Options) (*QueryPlan, error)
   333  }
   334  
   335  // QueryPlan is the response of an Explain query.
   336  type QueryPlan struct {
   337  	DBName   string                 `json:"dbname"`
   338  	Index    map[string]interface{} `json:"index"`
   339  	Selector map[string]interface{} `json:"selector"`
   340  	Options  map[string]interface{} `json:"opts"`
   341  	Limit    int64                  `json:"limit"`
   342  	Skip     int64                  `json:"skip"`
   343  
   344  	// Fields is the list of fields to be returned in the result set, or
   345  	// an empty list if all fields are to be returned.
   346  	Fields []interface{}          `json:"fields"`
   347  	Range  map[string]interface{} `json:"range"`
   348  }
   349  
   350  // Index is a MonboDB-style index definition.
   351  type Index struct {
   352  	DesignDoc  string      `json:"ddoc,omitempty"`
   353  	Name       string      `json:"name"`
   354  	Type       string      `json:"type"`
   355  	Definition interface{} `json:"def"`
   356  }
   357  
   358  // Attachment represents a file attachment to a document.
   359  type Attachment struct {
   360  	Filename        string        `json:"-"`
   361  	ContentType     string        `json:"content_type"`
   362  	Stub            bool          `json:"stub"`
   363  	Follows         bool          `json:"follows"`
   364  	Content         io.ReadCloser `json:"-"`
   365  	Size            int64         `json:"length"`
   366  	ContentEncoding string        `json:"encoding"`
   367  	EncodedLength   int64         `json:"encoded_length"`
   368  	RevPos          int64         `json:"revpos"`
   369  	Digest          string        `json:"digest"`
   370  }
   371  
   372  // AttachmentMetaGetter is an optional interface which may be implemented by a
   373  // [DB]. When not implemented, [DB.GetAttachment] will be used to emulate the
   374  // functionality.
   375  type AttachmentMetaGetter interface {
   376  	// GetAttachmentMeta returns meta information about an attachment.
   377  	GetAttachmentMeta(ctx context.Context, docID, filename string, options Options) (*Attachment, error)
   378  }
   379  
   380  // BulkResult is the result of a single doc update in a BulkDocs request.
   381  type BulkResult struct {
   382  	ID    string `json:"id"`
   383  	Rev   string `json:"rev"`
   384  	Error error
   385  }
   386  
   387  // RevGetter is an optional interface that may be implemented by a [DB]. If not
   388  // implemented, [DB.Get] will be used to emulate the functionality, with options
   389  // passed through unaltered.
   390  type RevGetter interface {
   391  	// GetRev returns the document revision of the requested document. GetRev
   392  	// should accept the same options as [DB.Get].
   393  	GetRev(ctx context.Context, docID string, options Options) (rev string, err error)
   394  }
   395  
   396  // Flusher is an optional interface that may be implemented by a [DB] that can
   397  // force a flush of the database backend file(s) to disk or other permanent
   398  // storage.
   399  type Flusher interface {
   400  	// Flush requests a flush of disk cache to disk or other permanent storage.
   401  	//
   402  	// See the [CouchDB documentation].
   403  	//
   404  	// [CouchDB documentation]: http://docs.couchdb.org/en/2.0.0/api/database/compact.html#db-ensure-full-commit
   405  	Flush(ctx context.Context) error
   406  }
   407  
   408  // Copier is an optional interface that may be implemented by a [DB].
   409  //
   410  // If a [DB] does not implement Copier, the functionality will be emulated by
   411  // calling [DB.Get] followed by [DB.Put], with options passed through unaltered,
   412  // except that the 'rev' option will be removed for the [DB.Put] call.
   413  type Copier interface {
   414  	Copy(ctx context.Context, targetID, sourceID string, options Options) (targetRev string, err error)
   415  }
   416  
   417  // DesignDocer is an optional interface that may be implemented by a [DB].
   418  type DesignDocer interface {
   419  	// DesignDocs returns all of the design documents in the database, subject
   420  	// to the options provided.
   421  	DesignDocs(ctx context.Context, options Options) (Rows, error)
   422  }
   423  
   424  // LocalDocer is an optional interface that may be implemented by a [DB].
   425  type LocalDocer interface {
   426  	// LocalDocs returns all of the local documents in the database, subject to
   427  	// the options provided.
   428  	LocalDocs(ctx context.Context, options Options) (Rows, error)
   429  }
   430  
   431  // Pinger is an optional interface that may be implemented by a [Client]. When
   432  // not implemented, Kivik will call [Client.Version] instead to emulate the
   433  // functionality.
   434  type Pinger interface {
   435  	// Ping returns true if the database is online and available for requests.
   436  	Ping(ctx context.Context) (bool, error)
   437  }
   438  
   439  // ClusterMembership contains the list of known nodes, and cluster nodes, as
   440  // returned by the [_membership endpoint].
   441  //
   442  // [_membership endpoint]: https://docs.couchdb.org/en/latest/api/server/common.html#get--_membership
   443  type ClusterMembership struct {
   444  	AllNodes     []string `json:"all_nodes"`
   445  	ClusterNodes []string `json:"cluster_nodes"`
   446  }
   447  
   448  // Cluster is an optional interface that may be implemented by a [Client] to
   449  // support CouchDB cluster configuration operations.
   450  type Cluster interface {
   451  	// ClusterStatus returns the current cluster status.
   452  	ClusterStatus(ctx context.Context, options Options) (string, error)
   453  	// ClusterSetup performs the action specified by action.
   454  	ClusterSetup(ctx context.Context, action interface{}) error
   455  	// Membership returns a list of all known nodes, and all nodes configured as
   456  	// part of the cluster.
   457  	Membership(ctx context.Context) (*ClusterMembership, error)
   458  }
   459  
   460  // ClientCloser is an optional interface that may be implemented by a [Client]
   461  // to clean up resources when a client is no longer needed.
   462  type ClientCloser interface {
   463  	Close() error
   464  }
   465  
   466  // RevDiff represents a rev diff for a single document, as returned by
   467  // [RevsDiffer.RevsDiff].
   468  type RevDiff struct {
   469  	Missing           []string `json:"missing,omitempty"`
   470  	PossibleAncestors []string `json:"possible_ancestors,omitempty"`
   471  }
   472  
   473  // RevsDiffer is an optional interface that may be implemented by a [DB].
   474  type RevsDiffer interface {
   475  	// RevsDiff returns a Rows iterator, which should populate the ID and Value
   476  	// fields, and nothing else.
   477  	RevsDiff(ctx context.Context, revMap interface{}) (Rows, error)
   478  }
   479  

View as plain text