1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #ident "$Id$"
3 /*======
4 This file is part of PerconaFT.
5 
6 
7 Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
8 
9     PerconaFT is free software: you can redistribute it and/or modify
10     it under the terms of the GNU General Public License, version 2,
11     as published by the Free Software Foundation.
12 
13     PerconaFT is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with PerconaFT.  If not, see <http://www.gnu.org/licenses/>.
20 
21 ----------------------------------------
22 
23     PerconaFT is free software: you can redistribute it and/or modify
24     it under the terms of the GNU Affero General Public License, version 3,
25     as published by the Free Software Foundation.
26 
27     PerconaFT is distributed in the hope that it will be useful,
28     but WITHOUT ANY WARRANTY; without even the implied warranty of
29     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30     GNU Affero General Public License for more details.
31 
32     You should have received a copy of the GNU Affero General Public License
33     along with PerconaFT.  If not, see <http://www.gnu.org/licenses/>.
34 ======= */
35 
36 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
37 
38 #pragma once
39 
40 #include <utility>
41 
42 #include <db.h>
43 
44 #include "buffer.hpp"
45 #include "db.hpp"
46 #include "db_env.hpp"
47 #include "db_txn.hpp"
48 #include "slice.hpp"
49 
50 namespace ftcxx {
51 
52     class DB;
53 
54     struct IterationStrategy {
55         bool forward;
56         bool prelock;
57 
IterationStrategyftcxx::IterationStrategy58         IterationStrategy(bool forward_, bool prelock_)
59             : forward(forward_),
60               prelock(prelock_)
61         {}
62 
getf_flagsftcxx::IterationStrategy63         int getf_flags() const {
64             if (prelock) {
65                 return DB_PRELOCKED | DB_PRELOCKED_WRITE;
66             } else {
67                 return DBC_DISABLE_PREFETCHING;
68             }
69         }
70     };
71 
72     class Bounds {
73         const ::DB *_db;
74         Slice _left;
75         Slice _right;
76         DBT _left_dbt;
77         DBT _right_dbt;
78         bool _left_infinite;
79         bool _right_infinite;
80         bool _end_exclusive;
81 
82     public:
Bounds(const DB & db,const Slice & left,const Slice & right,bool end_exclusive)83         Bounds(const DB &db, const Slice &left, const Slice &right, bool end_exclusive)
84             : _db(db.db()),
85               _left(left.owned()),
86               _right(right.owned()),
87               _left_dbt(_left.dbt()),
88               _right_dbt(_right.dbt()),
89               _left_infinite(false),
90               _right_infinite(false),
91               _end_exclusive(end_exclusive)
92         {}
93 
94         struct Infinite {};
95 
Bounds(const DB & db,Infinite,const Slice & right,bool end_exclusive)96         Bounds(const DB &db, Infinite, const Slice &right, bool end_exclusive)
97             : _db(db.db()),
98               _left(),
99               _right(right.owned()),
100               _left_dbt(_left.dbt()),
101               _right_dbt(_right.dbt()),
102               _left_infinite(true),
103               _right_infinite(false),
104               _end_exclusive(end_exclusive)
105         {}
106 
Bounds(const DB & db,const Slice & left,Infinite,bool end_exclusive)107         Bounds(const DB &db, const Slice &left, Infinite, bool end_exclusive)
108             : _db(db.db()),
109               _left(left.owned()),
110               _right(),
111               _left_dbt(_left.dbt()),
112               _right_dbt(_right.dbt()),
113               _left_infinite(false),
114               _right_infinite(true),
115               _end_exclusive(end_exclusive)
116         {}
117 
Bounds(const DB & db,Infinite,Infinite,bool end_exclusive)118         Bounds(const DB &db, Infinite, Infinite, bool end_exclusive)
119             : _db(db.db()),
120               _left(),
121               _right(),
122               _left_dbt(_left.dbt()),
123               _right_dbt(_right.dbt()),
124               _left_infinite(true),
125               _right_infinite(true),
126               _end_exclusive(end_exclusive)
127         {}
128 
129         Bounds(const Bounds &other) = delete;
130         Bounds& operator=(const Bounds &) = delete;
131 
Bounds(Bounds && o)132         Bounds(Bounds &&o)
133             : _db(nullptr),
134               _left(),
135               _right(),
136               _left_infinite(o._left_infinite),
137               _right_infinite(o._right_infinite),
138               _end_exclusive(o._end_exclusive)
139         {
140             std::swap(_db, o._db);
141             std::swap(_left, o._left);
142             std::swap(_right, o._right);
143             _left_dbt = _left.dbt();
144             _right_dbt = _right.dbt();
145         }
146 
operator =(Bounds && other)147         Bounds& operator=(Bounds&& other) {
148             std::swap(_db, other._db);
149             std::swap(_left, other._left);
150             std::swap(_right, other._right);
151             _left_dbt = _left.dbt();
152             _right_dbt = _right.dbt();
153             _left_infinite = other._left_infinite;
154             _right_infinite = other._right_infinite;
155             _end_exclusive = other._end_exclusive;
156             return *this;
157         }
158 
left_dbt() const159         const DBT *left_dbt() const {
160             if (_left_infinite) {
161                 return _db->dbt_neg_infty();
162             } else {
163                 return &_left_dbt;
164             }
165         }
166 
right_dbt() const167         const DBT *right_dbt() const {
168             if (_right_infinite) {
169                 return _db->dbt_pos_infty();
170             } else {
171                 return &_right_dbt;
172             }
173         }
174 
set_left(const Slice & left)175         void set_left(const Slice &left) {
176             _left = left.owned();
177             _left_dbt = _left.dbt();
178             _left_infinite = false;
179         }
180 
set_right(const Slice & right)181         void set_right(const Slice &right) {
182             _right = right.owned();
183             _right_dbt = _right.dbt();
184             _right_infinite = false;
185         }
186 
left_infinite() const187         bool left_infinite() const { return _left_infinite; }
right_infinite() const188         bool right_infinite() const { return _right_infinite; }
189 
190         template<class Comparator>
191         bool check(Comparator &cmp, const IterationStrategy &strategy, const Slice &key) const;
192     };
193 
194     /**
195      * DBC is a simple RAII wrapper around a DBC object.
196      */
197     class DBC {
198     public:
199         DBC(const DB &db, const DBTxn &txn=DBTxn(), int flags=0);
200         ~DBC();
201 
202         // Directory cursor.
203         DBC(const DBEnv &env, const DBTxn &txn=DBTxn());
204 
205         DBC(const DBC &) = delete;
206         DBC& operator=(const DBC &) = delete;
207 
DBC(DBC && o)208         DBC(DBC &&o)
209             : _txn(),
210               _dbc(nullptr)
211         {
212             std::swap(_txn, o._txn);
213             std::swap(_dbc, o._dbc);
214         }
215 
operator =(DBC && o)216         DBC& operator=(DBC &&o) {
217             std::swap(_txn, o._txn);
218             std::swap(_dbc, o._dbc);
219             return *this;
220         }
221 
dbc() const222         ::DBC *dbc() const { return _dbc; }
223 
set_txn(const DBTxn & txn) const224         void set_txn(const DBTxn &txn) const {
225             _dbc->c_set_txn(_dbc, txn.txn());
226         }
227 
228         void close();
229 
230         bool set_range(const IterationStrategy &strategy, const Bounds &bounds, YDB_CALLBACK_FUNCTION callback, void *extra) const;
231 
232         bool advance(const IterationStrategy &strategy, YDB_CALLBACK_FUNCTION callback, void *extra) const;
233 
234     protected:
235 
236         // the ordering here matters, for destructors
237         DBTxn _txn;
238         ::DBC *_dbc;
239     };
240 
241     /**
242      * Cursor supports iterating a cursor over a key range,
243      * with bulk fetch buffering, and optional filtering.
244      */
245     template<class Comparator, class Handler>
246     class CallbackCursor {
247     public:
248 
249         /**
250          * Directory cursor.
251          */
252         CallbackCursor(const DBEnv &env, const DBTxn &txn,
253                        Comparator &&cmp, Handler &&handler);
254 
255         /**
256          * Constructs an cursor.  Better to use DB::cursor instead to
257          * avoid template parameters.
258          */
259         CallbackCursor(const DB &db, const DBTxn &txn, int flags,
260                        IterationStrategy iteration_strategy,
261                        Bounds bounds,
262                        Comparator &&cmp, Handler &&handler);
263 
264         /**
265          * Gets the next key/val pair in the iteration.  Returns true
266          * if there is more data, and fills in key and val.  If the
267          * range is exhausted, returns false.
268          */
269         bool consume_batch();
270 
271         void seek(const Slice &key);
272 
finished() const273         bool finished() const { return _finished; }
274 
ok() const275         bool ok() const { return !finished(); }
276 
set_txn(const DBTxn & txn) const277         void set_txn(const DBTxn &txn) const { _dbc.set_txn(txn); }
278 
279     private:
280 
281         DBC _dbc;
282         IterationStrategy _iteration_strategy;
283         Bounds _bounds;
284         Comparator _cmp;
285         Handler _handler;
286 
287         bool _finished;
288 
289         void init();
290 
getf_callback(const DBT * key,const DBT * val,void * extra)291         static int getf_callback(const DBT *key, const DBT *val, void *extra) {
292             CallbackCursor *i = static_cast<CallbackCursor *>(extra);
293             return i->getf(key, val);
294         }
295 
296         int getf(const DBT *key, const DBT *val);
297     };
298 
299     template<class Predicate>
300     class BufferAppender {
301         Buffer &_buf;
302         Predicate _filter;
303 
304     public:
BufferAppender(Buffer & buf,Predicate && filter)305         BufferAppender(Buffer &buf, Predicate &&filter)
306             : _buf(buf),
307               _filter(std::forward<Predicate>(filter))
308         {}
309 
310         bool operator()(const DBT *key, const DBT *val);
311 
marshalled_size(size_t keylen,size_t vallen)312         static size_t marshalled_size(size_t keylen, size_t vallen) {
313             return (sizeof(((DBT *)0)->size)) + (sizeof(((DBT *)0)->size)) + keylen + vallen;
314         }
315 
316         static void marshall(char *dest, const DBT *key, const DBT *val);
317 
318         static void unmarshall(char *src, DBT *key, DBT *val);
319         static void unmarshall(char *src, Slice &key, Slice &val);
320     };
321 
322     template<class Comparator, class Predicate>
323     class BufferedCursor {
324     public:
325 
326         /**
327          * Directory cursor.
328          */
329         BufferedCursor(const DBEnv &env, const DBTxn &txn,
330                        Comparator &&cmp, Predicate &&filter);
331 
332         /**
333          * Constructs an buffered cursor.  Better to use
334          * DB::buffered_cursor instead to avoid template parameters.
335          */
336         BufferedCursor(const DB &db, const DBTxn &txn, int flags,
337                        IterationStrategy iteration_strategy,
338                        Bounds bounds,
339                        Comparator &&cmp, Predicate &&filter);
340 
341         /**
342          * Gets the next key/val pair in the iteration.  Returns true
343          * if there is more data, and fills in key and val.  If the
344          * range is exhausted, returns false.
345          */
346         bool next(DBT *key, DBT *val);
347         bool next(Slice &key, Slice &val);
348 
349         void seek(const Slice &key);
350 
ok() const351         bool ok() const {
352             return _cur.ok() || _buf.more();
353         }
354 
set_txn(const DBTxn & txn) const355         void set_txn(const DBTxn &txn) const { _cur.set_txn(txn); }
356 
357     private:
358 
359         typedef BufferAppender<Predicate> Appender;
360 
361         Buffer _buf;
362         CallbackCursor<Comparator, Appender> _cur;
363     };
364 
365     template<class Comparator>
366     class SimpleCursor {
367     public:
368         SimpleCursor(const DBEnv &env, const DBTxn &txn,
369                      Comparator &&cmp, Slice &key, Slice &val);
370 
371         SimpleCursor(const DB &db, const DBTxn &txn, int flags,
372                      IterationStrategy iteration_strategy,
373                      Bounds bounds, Comparator &&cmp,
374                      Slice &key, Slice &val);
375 
376         /**
377          * Gets the next key/val pair in the iteration.  Copies data
378          * directly into key and val, which will own their buffers.
379          */
380         bool next();
381 
382         void seek(const Slice &key);
383 
ok() const384         bool ok() const {
385             return _cur.ok();
386         }
387 
set_txn(const DBTxn & txn) const388         void set_txn(const DBTxn &txn) const { _cur.set_txn(txn); }
389 
390         class SliceCopier {
391             Slice &_key;
392             Slice &_val;
393 
394         public:
SliceCopier(Slice & key,Slice & val)395             SliceCopier(Slice &key, Slice &val)
396                 : _key(key),
397                   _val(val)
398             {}
399 
operator ()(const DBT * key,const DBT * val)400             bool operator()(const DBT *key, const DBT *val) {
401                 _key = Slice(*key).owned();
402                 _val = Slice(*val).owned();
403 
404                 // Don't bulk fetch.
405                 return false;
406             }
407         };
408 
409     private:
410 
411         SliceCopier _copier;
412         CallbackCursor<Comparator, SliceCopier&> _cur;
413     };
414 
415 } // namespace ftcxx
416 
417 #include "cursor-inl.hpp"
418