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 }