...

Source file src/github.com/go-kivik/kivik/v4/x/memorydb/db.go

Documentation: github.com/go-kivik/kivik/v4/x/memorydb

     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 memorydb
    14  
    15  import (
    16  	"bytes"
    17  	"context"
    18  	"errors"
    19  	"fmt"
    20  	"io"
    21  	"net/http"
    22  	"regexp"
    23  	"strings"
    24  
    25  	"github.com/go-kivik/kivik/v4"
    26  	"github.com/go-kivik/kivik/v4/driver"
    27  )
    28  
    29  var notYetImplemented = statusError{status: http.StatusNotImplemented, error: errors.New("kivik: not yet implemented in memory driver")}
    30  
    31  // database is an in-memory database representation.
    32  type db struct {
    33  	*client
    34  	dbName string
    35  	db     *database
    36  }
    37  
    38  func (d *db) Query(context.Context, string, string, driver.Options) (driver.Rows, error) {
    39  	// FIXME: Unimplemented
    40  	return nil, notYetImplemented
    41  }
    42  
    43  func (d *db) Get(ctx context.Context, docID string, options driver.Options) (*driver.Document, error) {
    44  	if exists, _ := d.client.DBExists(ctx, d.dbName, nil); !exists {
    45  		return nil, statusError{status: http.StatusPreconditionFailed, error: errors.New("database does not exist")}
    46  	}
    47  	if !d.db.docExists(docID) {
    48  		return nil, statusError{status: http.StatusNotFound, error: errors.New("missing")}
    49  	}
    50  	opts := map[string]interface{}{}
    51  	options.Apply(opts)
    52  	if rev, ok := opts["rev"].(string); ok {
    53  		if doc, found := d.db.getRevision(docID, rev); found {
    54  			return &driver.Document{
    55  				Rev:  rev,
    56  				Body: io.NopCloser(bytes.NewReader(doc.data)),
    57  			}, nil
    58  		}
    59  		return nil, statusError{status: http.StatusNotFound, error: errors.New("missing")}
    60  	}
    61  	last, _ := d.db.latestRevision(docID)
    62  	if last.Deleted {
    63  		return nil, statusError{status: http.StatusNotFound, error: errors.New("missing")}
    64  	}
    65  	return &driver.Document{
    66  		Rev:  fmt.Sprintf("%d-%s", last.ID, last.Rev),
    67  		Body: io.NopCloser(bytes.NewReader(last.data)),
    68  	}, nil
    69  }
    70  
    71  func (d *db) Put(ctx context.Context, docID string, doc interface{}, _ driver.Options) (rev string, err error) {
    72  	if exists, _ := d.client.DBExists(ctx, d.dbName, nil); !exists {
    73  		return "", statusError{status: http.StatusPreconditionFailed, error: errors.New("database does not exist")}
    74  	}
    75  	isLocal := strings.HasPrefix(docID, "_local/")
    76  	if !isLocal && docID[0] == '_' && !strings.HasPrefix(docID, "_design/") {
    77  		return "", statusError{status: http.StatusBadRequest, error: errors.New("only reserved document ids may start with underscore")}
    78  	}
    79  	couchDoc, err := toCouchDoc(doc)
    80  	if err != nil {
    81  		return "", err
    82  	}
    83  	couchDoc["_id"] = docID
    84  	// TODO: Add support for storing attachments.
    85  	delete(couchDoc, "_attachments")
    86  
    87  	if last, ok := d.db.latestRevision(docID); ok {
    88  		if !last.Deleted && !isLocal && couchDoc.Rev() != fmt.Sprintf("%d-%s", last.ID, last.Rev) {
    89  			return "", statusError{status: http.StatusConflict, error: errors.New("document update conflict")}
    90  		}
    91  		return d.db.addRevision(couchDoc), nil
    92  	}
    93  
    94  	if couchDoc.Rev() != "" {
    95  		// Rev should not be set for a new document
    96  		return "", statusError{status: http.StatusConflict, error: errors.New("document update conflict")}
    97  	}
    98  	return d.db.addRevision(couchDoc), nil
    99  }
   100  
   101  var revRE = regexp.MustCompile("^[0-9]+-[a-f0-9]{32}$")
   102  
   103  func validRev(rev string) bool {
   104  	return revRE.MatchString(rev)
   105  }
   106  
   107  func (d *db) Delete(ctx context.Context, docID string, options driver.Options) (newRev string, err error) {
   108  	if exists, _ := d.client.DBExists(ctx, d.dbName, kivik.Params(nil)); !exists {
   109  		return "", statusError{status: http.StatusPreconditionFailed, error: errors.New("database does not exist")}
   110  	}
   111  	opts := map[string]interface{}{}
   112  	options.Apply(opts)
   113  	rev, _ := opts["rev"].(string)
   114  	if !strings.HasPrefix(docID, "_local/") && !validRev(rev) {
   115  		return "", statusError{status: http.StatusBadRequest, error: errors.New("invalid rev format")}
   116  	}
   117  	if !d.db.docExists(docID) {
   118  		return "", statusError{status: http.StatusNotFound, error: errors.New("missing")}
   119  	}
   120  	return d.Put(ctx, docID, map[string]interface{}{
   121  		"_id":      docID,
   122  		"_rev":     rev,
   123  		"_deleted": true,
   124  	}, nil)
   125  }
   126  
   127  func (d *db) Stats(_ context.Context) (*driver.DBStats, error) {
   128  	return &driver.DBStats{
   129  		Name: d.dbName,
   130  		// DocCount:     0,
   131  		// DeletedCount: 0,
   132  		// UpdateSeq:    "",
   133  		// DiskSize:     0,
   134  		// ActiveSize:   0,
   135  		// ExternalSize: 0,
   136  	}, nil
   137  }
   138  
   139  func (c *client) Compact(_ context.Context) error {
   140  	// FIXME: Unimplemented
   141  	return notYetImplemented
   142  }
   143  
   144  func (d *db) CompactView(_ context.Context, _ string) error {
   145  	// FIXME: Unimplemented
   146  	return notYetImplemented
   147  }
   148  
   149  func (d *db) ViewCleanup(_ context.Context) error {
   150  	// FIXME: Unimplemented
   151  	return notYetImplemented
   152  }
   153  
   154  func (d *db) Changes(context.Context, driver.Options) (driver.Changes, error) {
   155  	// FIXME: Unimplemented
   156  	return nil, notYetImplemented
   157  }
   158  
   159  func (d *db) PutAttachment(context.Context, string, *driver.Attachment, driver.Options) (string, error) {
   160  	// FIXME: Unimplemented
   161  	return "", notYetImplemented
   162  }
   163  
   164  func (d *db) GetAttachment(context.Context, string, string, driver.Options) (*driver.Attachment, error) {
   165  	// FIXME: Unimplemented
   166  	return nil, notYetImplemented
   167  }
   168  
   169  func (d *db) DeleteAttachment(context.Context, string, string, driver.Options) (newRev string, err error) {
   170  	// FIXME: Unimplemented
   171  	return "", notYetImplemented
   172  }
   173  
   174  func (d *db) Close() error { return nil }
   175  

View as plain text