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 | |
58 | IterationStrategy(bool forward_, bool prelock_) |
59 | : forward(forward_), |
60 | prelock(prelock_) |
61 | {} |
62 | |
63 | 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: |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
159 | const DBT *left_dbt() const { |
160 | if (_left_infinite) { |
161 | return _db->dbt_neg_infty(); |
162 | } else { |
163 | return &_left_dbt; |
164 | } |
165 | } |
166 | |
167 | const DBT *right_dbt() const { |
168 | if (_right_infinite) { |
169 | return _db->dbt_pos_infty(); |
170 | } else { |
171 | return &_right_dbt; |
172 | } |
173 | } |
174 | |
175 | void set_left(const Slice &left) { |
176 | _left = left.owned(); |
177 | _left_dbt = _left.dbt(); |
178 | _left_infinite = false; |
179 | } |
180 | |
181 | void set_right(const Slice &right) { |
182 | _right = right.owned(); |
183 | _right_dbt = _right.dbt(); |
184 | _right_infinite = false; |
185 | } |
186 | |
187 | bool left_infinite() const { return _left_infinite; } |
188 | 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 | |
208 | DBC(DBC &&o) |
209 | : _txn(), |
210 | _dbc(nullptr) |
211 | { |
212 | std::swap(_txn, o._txn); |
213 | std::swap(_dbc, o._dbc); |
214 | } |
215 | |
216 | DBC& operator=(DBC &&o) { |
217 | std::swap(_txn, o._txn); |
218 | std::swap(_dbc, o._dbc); |
219 | return *this; |
220 | } |
221 | |
222 | ::DBC *dbc() const { return _dbc; } |
223 | |
224 | 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 *) const; |
231 | |
232 | bool advance(const IterationStrategy &strategy, YDB_CALLBACK_FUNCTION callback, void *) 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 | |
273 | bool finished() const { return _finished; } |
274 | |
275 | bool ok() const { return !finished(); } |
276 | |
277 | 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 | |
291 | static int getf_callback(const DBT *key, const DBT *val, void *) { |
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: |
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 | |
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 | |
351 | bool ok() const { |
352 | return _cur.ok() || _buf.more(); |
353 | } |
354 | |
355 | 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 | |
384 | bool ok() const { |
385 | return _cur.ok(); |
386 | } |
387 | |
388 | void set_txn(const DBTxn &txn) const { _cur.set_txn(txn); } |
389 | |
390 | class SliceCopier { |
391 | Slice &_key; |
392 | Slice &_val; |
393 | |
394 | public: |
395 | SliceCopier(Slice &key, Slice &val) |
396 | : _key(key), |
397 | _val(val) |
398 | {} |
399 | |
400 | bool operator()(const DBT *key, const DBT *val) { |
401 | _key = std::move(Slice(*key).owned()); |
402 | _val = std::move(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 | |