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

     1  /*
     2  Copyright 2012 Google Inc.
     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 sqlindex implements the sorted.KeyValue interface using an *sql.DB.
    18  package sqlindex
    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  // Storage implements the sorted.KeyValue interface using an *sql.DB.
    33  type Storage 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 (s *Storage) sql(v string) string {
    56  	if f := s.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 (s *Storage) BeginBatch() sorted.BatchMutation {
   100  	if s.Serial {
   101  		s.mu.Lock()
   102  	}
   103  	tx, err := s.DB.Begin()
   104  	return &batchTx{
   105  		tx:              tx,
   106  		err:             err,
   107  		SetFunc:         s.BatchSetFunc,
   108  		PlaceHolderFunc: s.PlaceHolderFunc,
   109  	}
   110  }
   111  
   112  func (s *Storage) CommitBatch(b sorted.BatchMutation) error {
   113  	if s.Serial {
   114  		defer s.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 (s *Storage) Get(key string) (value string, err error) {
   127  	if s.Serial {
   128  		s.mu.Lock()
   129  		defer s.mu.Unlock()
   130  	}
   131  	err = s.DB.QueryRow(s.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 (s *Storage) Set(key, value string) error {
   139  	if s.Serial {
   140  		s.mu.Lock()
   141  		defer s.mu.Unlock()
   142  	}
   143  	if s.SetFunc != nil {
   144  		return s.SetFunc(s.DB, key, value)
   145  	}
   146  	_, err := s.DB.Exec(s.sql("REPLACE INTO rows (k, v) VALUES (?, ?)"), key, value)
   147  	return err
   148  }
   149  
   150  func (s *Storage) Delete(key string) error {
   151  	if s.Serial {
   152  		s.mu.Lock()
   153  		defer s.mu.Unlock()
   154  	}
   155  	_, err := s.DB.Exec(s.sql("DELETE FROM rows WHERE k=?"), key)
   156  	return err
   157  }
   158  
   159  func (s *Storage) Close() error { return s.DB.Close() }
   160  
   161  func (s *Storage) Find(start, end string) sorted.Iterator {
   162  	if s.Serial {
   163  		s.mu.Lock()
   164  		defer s.mu.Unlock()
   165  	}
   166  	var rows *sql.Rows
   167  	var err error
   168  	if end == "" {
   169  		rows, err = s.DB.Query(s.sql("SELECT k, v FROM rows WHERE k >= ? ORDER BY k "), start)
   170  	} else {
   171  		rows, err = s.DB.Query(s.sql("SELECT k, v FROM rows WHERE k >= ? AND k < ? ORDER BY k "), start, end)
   172  	}
   173  	if err != nil {
   174  		log.Printf("unexpected query error: %v", err)
   175  		return &iter{err: err}
   176  	}
   177  
   178  	it := &iter{
   179  		s:          s,
   180  		rows:       rows,
   181  		closeCheck: leak.NewChecker(),
   182  	}
   183  	return it
   184  }
   185  
   186  var wordThenPunct = regexp.MustCompile(`^\w+\W$`)
   187  
   188  // iter is a iterator over sorted key/value pairs in rows.
   189  type iter struct {
   190  	s   *Storage
   191  	end string // optional end bound
   192  	err error  // accumulated error, returned at Close
   193  
   194  	closeCheck *leak.Checker
   195  
   196  	rows *sql.Rows // if non-nil, the rows we're reading from
   197  
   198  	key        sql.RawBytes
   199  	val        sql.RawBytes
   200  	skey, sval *string // if non-nil, it's been stringified
   201  }
   202  
   203  var errClosed = errors.New("sqlkv: Iterator already closed")
   204  
   205  func (t *iter) KeyBytes() []byte { return t.key }
   206  func (t *iter) Key() string {
   207  	if t.skey != nil {
   208  		return *t.skey
   209  	}
   210  	str := string(t.key)
   211  	t.skey = &str
   212  	return str
   213  }
   214  
   215  func (t *iter) ValueBytes() []byte { return t.val }
   216  func (t *iter) Value() string {
   217  	if t.sval != nil {
   218  		return *t.sval
   219  	}
   220  	str := string(t.val)
   221  	t.sval = &str
   222  	return str
   223  }
   224  
   225  func (t *iter) Close() error {
   226  	t.closeCheck.Close()
   227  	if t.rows != nil {
   228  		t.rows.Close()
   229  		t.rows = nil
   230  	}
   231  	err := t.err
   232  	t.err = errClosed
   233  	return err
   234  }
   235  
   236  func (t *iter) Next() bool {
   237  	if t.err != nil {
   238  		return false
   239  	}
   240  	t.skey, t.sval = nil, nil
   241  	if !t.rows.Next() {
   242  		return false
   243  	}
   244  	t.err = t.rows.Scan(&t.key, &t.val)
   245  	if t.err != nil {
   246  		log.Printf("unexpected Scan error: %v", t.err)
   247  		return false
   248  	}
   249  	return true
   250  }