github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/sorted/sqlkv/sqlkv.go (about)

     1  /*
     2  Copyright 2012 The Camlistore Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package sqlkv implements the sorted.KeyValue interface using an *sql.DB.
    18  package sqlkv
    19  
    20  import (
    21  	"database/sql"
    22  	"errors"
    23  	"fmt"
    24  	"log"
    25  	"regexp"
    26  	"sync"
    27  
    28  	"camlistore.org/pkg/leak"
    29  	"camlistore.org/pkg/sorted"
    30  )
    31  
    32  // KeyValue implements the sorted.KeyValue interface using an *sql.DB.
    33  type KeyValue struct {
    34  	DB *sql.DB
    35  
    36  	// SetFunc is an optional func to use when REPLACE INTO does not exist
    37  	SetFunc      func(*sql.DB, string, string) error
    38  	BatchSetFunc func(*sql.Tx, string, string) error
    39  
    40  	// PlaceHolderFunc optionally replaces ? placeholders with the right ones for the rdbms
    41  	// in use
    42  	PlaceHolderFunc func(string) string
    43  
    44  	// Serial determines whether a Go-level mutex protects DB from
    45  	// concurrent access.  This isn't perfect and exists just for
    46  	// SQLite, whose driver likes to return "the database is
    47  	// locked" (camlistore.org/issue/114), so this keeps some
    48  	// pressure off. But we still trust SQLite to deal with
    49  	// concurrency in most cases.
    50  	Serial bool
    51  
    52  	mu sync.Mutex // the mutex used, if Serial is set
    53  }
    54  
    55  func (kv *KeyValue) sql(v string) string {
    56  	if f := kv.PlaceHolderFunc; f != nil {
    57  		return f(v)
    58  	}
    59  	return v
    60  }
    61  
    62  type batchTx struct {
    63  	tx  *sql.Tx
    64  	err error // sticky
    65  
    66  	// SetFunc is an optional func to use when REPLACE INTO does not exist
    67  	SetFunc func(*sql.Tx, string, string) error
    68  
    69  	// PlaceHolderFunc optionally replaces ? placeholders with the right ones for the rdbms
    70  	// in use
    71  	PlaceHolderFunc func(string) string
    72  }
    73  
    74  func (b *batchTx) sql(v string) string {
    75  	if f := b.PlaceHolderFunc; f != nil {
    76  		return f(v)
    77  	}
    78  	return v
    79  }
    80  
    81  func (b *batchTx) Set(key, value string) {
    82  	if b.err != nil {
    83  		return
    84  	}
    85  	if b.SetFunc != nil {
    86  		b.err = b.SetFunc(b.tx, key, value)
    87  		return
    88  	}
    89  	_, b.err = b.tx.Exec(b.sql("REPLACE INTO rows (k, v) VALUES (?, ?)"), key, value)
    90  }
    91  
    92  func (b *batchTx) Delete(key string) {
    93  	if b.err != nil {
    94  		return
    95  	}
    96  	_, b.err = b.tx.Exec(b.sql("DELETE FROM rows WHERE k=?"), key)
    97  }
    98  
    99  func (kv *KeyValue) BeginBatch() sorted.BatchMutation {
   100  	if kv.Serial {
   101  		kv.mu.Lock()
   102  	}
   103  	tx, err := kv.DB.Begin()
   104  	return &batchTx{
   105  		tx:              tx,
   106  		err:             err,
   107  		SetFunc:         kv.BatchSetFunc,
   108  		PlaceHolderFunc: kv.PlaceHolderFunc,
   109  	}
   110  }
   111  
   112  func (kv *KeyValue) CommitBatch(b sorted.BatchMutation) error {
   113  	if kv.Serial {
   114  		defer kv.mu.Unlock()
   115  	}
   116  	bt, ok := b.(*batchTx)
   117  	if !ok {
   118  		return fmt.Errorf("wrong BatchMutation type %T", b)
   119  	}
   120  	if bt.err != nil {
   121  		return bt.err
   122  	}
   123  	return bt.tx.Commit()
   124  }
   125  
   126  func (kv *KeyValue) Get(key string) (value string, err error) {
   127  	if kv.Serial {
   128  		kv.mu.Lock()
   129  		defer kv.mu.Unlock()
   130  	}
   131  	err = kv.DB.QueryRow(kv.sql("SELECT v FROM rows WHERE k=?"), key).Scan(&value)
   132  	if err == sql.ErrNoRows {
   133  		err = sorted.ErrNotFound
   134  	}
   135  	return
   136  }
   137  
   138  func (kv *KeyValue) Set(key, value string) error {
   139  	if kv.Serial {
   140  		kv.mu.Lock()
   141  		defer kv.mu.Unlock()
   142  	}
   143  	if kv.SetFunc != nil {
   144  		return kv.SetFunc(kv.DB, key, value)
   145  	}
   146  	_, err := kv.DB.Exec(kv.sql("REPLACE INTO rows (k, v) VALUES (?, ?)"), key, value)
   147  	return err
   148  }
   149  
   150  func (kv *KeyValue) Delete(key string) error {
   151  	if kv.Serial {
   152  		kv.mu.Lock()
   153  		defer kv.mu.Unlock()
   154  	}
   155  	_, err := kv.DB.Exec(kv.sql("DELETE FROM rows WHERE k=?"), key)
   156  	return err
   157  }
   158  
   159  func (kv *KeyValue) Wipe() error {
   160  	if kv.Serial {
   161  		kv.mu.Lock()
   162  		defer kv.mu.Unlock()
   163  	}
   164  	_, err := kv.DB.Exec(kv.sql("DELETE FROM rows"))
   165  	return err
   166  }
   167  
   168  func (kv *KeyValue) Close() error { return kv.DB.Close() }
   169  
   170  func (kv *KeyValue) Find(start, end string) sorted.Iterator {
   171  	if kv.Serial {
   172  		kv.mu.Lock()
   173  		defer kv.mu.Unlock()
   174  	}
   175  	var rows *sql.Rows
   176  	var err error
   177  	if end == "" {
   178  		rows, err = kv.DB.Query(kv.sql("SELECT k, v FROM rows WHERE k >= ? ORDER BY k "), start)
   179  	} else {
   180  		rows, err = kv.DB.Query(kv.sql("SELECT k, v FROM rows WHERE k >= ? AND k < ? ORDER BY k "), start, end)
   181  	}
   182  	if err != nil {
   183  		log.Printf("unexpected query error: %v", err)
   184  		return &iter{err: err}
   185  	}
   186  
   187  	it := &iter{
   188  		kv:         kv,
   189  		rows:       rows,
   190  		closeCheck: leak.NewChecker(),
   191  	}
   192  	return it
   193  }
   194  
   195  var wordThenPunct = regexp.MustCompile(`^\w+\W$`)
   196  
   197  // iter is a iterator over sorted key/value pairs in rows.
   198  type iter struct {
   199  	kv  *KeyValue
   200  	end string // optional end bound
   201  	err error  // accumulated error, returned at Close
   202  
   203  	closeCheck *leak.Checker
   204  
   205  	rows *sql.Rows // if non-nil, the rows we're reading from
   206  
   207  	key        sql.RawBytes
   208  	val        sql.RawBytes
   209  	skey, sval *string // if non-nil, it's been stringified
   210  }
   211  
   212  var errClosed = errors.New("sqlkv: Iterator already closed")
   213  
   214  func (t *iter) KeyBytes() []byte { return t.key }
   215  func (t *iter) Key() string {
   216  	if t.skey != nil {
   217  		return *t.skey
   218  	}
   219  	str := string(t.key)
   220  	t.skey = &str
   221  	return str
   222  }
   223  
   224  func (t *iter) ValueBytes() []byte { return t.val }
   225  func (t *iter) Value() string {
   226  	if t.sval != nil {
   227  		return *t.sval
   228  	}
   229  	str := string(t.val)
   230  	t.sval = &str
   231  	return str
   232  }
   233  
   234  func (t *iter) Close() error {
   235  	t.closeCheck.Close()
   236  	if t.rows != nil {
   237  		t.rows.Close()
   238  		t.rows = nil
   239  	}
   240  	err := t.err
   241  	t.err = errClosed
   242  	return err
   243  }
   244  
   245  func (t *iter) Next() bool {
   246  	if t.err != nil {
   247  		return false
   248  	}
   249  	t.skey, t.sval = nil, nil
   250  	if !t.rows.Next() {
   251  		return false
   252  	}
   253  	t.err = t.rows.Scan(&t.key, &t.val)
   254  	if t.err != nil {
   255  		log.Printf("unexpected Scan error: %v", t.err)
   256  		return false
   257  	}
   258  	return true
   259  }