summaryrefslogtreecommitdiff
path: root/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBCursor.java
blob: 47f6cb8b822a8ad9eac166ce3f246147ff6f3e6f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
/*
 * Copyright 2006-2012 Amazon Technologies, Inc. or its affiliates.
 * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
 * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.amazon.carbonado.repo.sleepycat;

import com.amazon.carbonado.FetchException;
import com.amazon.carbonado.IsolationLevel;
import com.amazon.carbonado.Storable;

import com.amazon.carbonado.raw.RawCursor;
import com.amazon.carbonado.raw.RawUtil;

import com.amazon.carbonado.txn.TransactionScope;

/**
 *
 *
 * @author Brian S O'Neill
 */
abstract class BDBCursor<Txn, S extends Storable> extends RawCursor<S> {
    private static final byte[] NO_DATA = new byte[0];

    private final TransactionScope<Txn> mScope;
    private final BDBStorage<Txn, S> mStorage;
    /**
     * @param scope
     * @param startBound specify the starting key for the cursor, or null if first
     * @param inclusiveStart true if start bound is inclusive
     * @param endBound specify the ending key for the cursor, or null if last
     * @param inclusiveEnd true if end bound is inclusive
     * @param maxPrefix maximum expected common initial bytes in start and end bound
     * @param reverse when true, iteration is reversed
     * @param storage
     * @throws IllegalArgumentException if any bound is null but is not inclusive
     * @throws ClassCastException if lock is not an object passed by
     * {@link BDBStorage#openCursor BDBStorage.openCursor}
     */
    protected BDBCursor(TransactionScope<Txn> scope,
                        byte[] startBound, boolean inclusiveStart,
                        byte[] endBound, boolean inclusiveEnd,
                        int maxPrefix,
                        boolean reverse,
                        BDBStorage<Txn, S> storage)
        throws FetchException
    {
        super(scope.getLock(),
              startBound, inclusiveStart,
              endBound, inclusiveEnd,
              maxPrefix, reverse);

        mScope = scope;
        mStorage = storage;
        scope.register(storage.getStorableType(), this);
    }

    void open() throws FetchException {
        try {
            cursor_open(mScope.getTxn(), mScope.getIsolationLevel());
        } catch (Exception e) {
            throw mStorage.toFetchException(e);
        }
    }

    @Override
    public void close() throws FetchException {
        try {
            super.close();
        } finally {
            mScope.unregister(mStorage.getStorableType(), this);
        }
    }

    @Override
    protected void release() throws FetchException {
        try {
            cursor_close();
        } catch (Exception e) {
            throw mStorage.toFetchException(e);
        }
    }

    @Override
    protected byte[] getCurrentKey() throws FetchException {
        if (searchKey_getPartial()) {
            throw new IllegalStateException();
        }
        return searchKey_getDataCopy();
    }

    @Override
    protected byte[] getCurrentValue() throws FetchException {
        if (data_getPartial()) {
            throw new IllegalStateException();
        }
        return data_getDataCopy();
    }

    @Override
    protected void disableKeyAndValue() {
        searchKey_setPartial(true);
        data_setPartial(true);
    }

    @Override
    protected void disableValue() {
        data_setPartial(true);
    }

    @Override
    protected void enableKeyAndValue() throws FetchException {
        searchKey_setPartial(false);
        data_setPartial(false);
        if (!hasCurrent()) {
            throw new FetchException("Current key and value missing");
        }
    }

    protected boolean hasCurrent() throws FetchException {
        try {
            return cursor_getCurrent();
        } catch (Exception e) {
            throw mStorage.toFetchException(e);
        }
    }

    @Override
    protected S instantiateCurrent() throws FetchException {
        return mStorage.instantiate(primaryKey_getData(), data_getData());
    }

    @Override
    protected boolean toFirst() throws FetchException {
        try {
            return cursor_getFirst();
        } catch (Exception e) {
            throw mStorage.toFetchException(e);
        }
    }

    @Override
    protected boolean toFirst(byte[] key) throws FetchException {
        try {
            searchKey_setData(key);
            return cursor_getSearchKeyRange();
        } catch (Exception e) {
            throw mStorage.toFetchException(e);
        }
    }

    @Override
    protected boolean toLast() throws FetchException {
        try {
            return cursor_getLast();
        } catch (Exception e) {
            throw mStorage.toFetchException(e);
        }
    }

    @Override
    protected boolean toLast(byte[] key) throws FetchException {
        try {
            // BDB cursor doesn't support "search for exact or less than", so
            // emulate it here. Add one to the key value, search, and then
            // back up.

            // This destroys the caller's key value. This method's
            // documentation indicates that the byte array may be altered.
            if (!RawUtil.increment(key)) {
                // This point is reached upon overflow, because key looked like:
                // 0xff, 0xff, 0xff, 0xff...
                // So moving to the absolute last is just fine.
                return cursor_getLast();
            }

            // Search for next record...
            searchKey_setData(key);
            if (cursor_getSearchKeyRange()) {
                // ...and back up.
                if (!cursor_getPrev()) {
                    return false;
                }
            } else {
                // Search found nothing, so go to the end.
                if (!cursor_getLast()) {
                    return false;
                }
            }

            // No guarantee that the currently matched key is correct, since
            // additional records may have been inserted after the search
            // operation finished.

            key = searchKey_getData();

            do {
                if (compareKeysPartially(searchKey_getData(), key) <= 0) {
                    return true;
                }
                // Keep backing up until the found key is equal or smaller than
                // what was requested.
            } while (cursor_getPrevNoDup());

            return false;
        } catch (Exception e) {
            throw mStorage.toFetchException(e);
        }
    }

    @Override
    protected boolean toNext() throws FetchException {
        try {
            return cursor_getNext();
        } catch (Exception e) {
            throw mStorage.toFetchException(e);
        }
    }

    @Override
    protected boolean toPrevious() throws FetchException {
        try {
            return cursor_getPrev();
        } catch (Exception e) {
            throw mStorage.toFetchException(e);
        }
    }

    /**
     * If the given byte array is less than or equal to given size, it is
     * simply returned. Otherwise, a new array is allocated and the data is
     * copied.
     */
    protected static byte[] getData(byte[] data, int size) {
        if (data == null) {
            return NO_DATA;
        }
        if (data.length <= size) {
            return data;
        }
        byte[] newData = new byte[size];
        System.arraycopy(data, 0, newData, 0, size);
        return newData;
    }

    /**
     * Returns a copy of the data array.
     */
    protected static byte[] getDataCopy(byte[] data, int size) {
        if (data == null) {
            return NO_DATA;
        }
        byte[] newData = new byte[size];
        System.arraycopy(data, 0, newData, 0, size);
        return newData;
    }

    @Override
    protected void handleNoSuchElement() throws FetchException {
        // Might not be any more elements because storage is closed.
        mStorage.checkClosed();
    }

    protected abstract byte[] searchKey_getData();

    protected abstract byte[] searchKey_getDataCopy();

    protected abstract void searchKey_setData(byte[] data);

    protected abstract void searchKey_setPartial(boolean partial);

    protected abstract boolean searchKey_getPartial();

    protected abstract byte[] data_getData();

    protected abstract byte[] data_getDataCopy();

    protected abstract void data_setPartial(boolean partial);

    protected abstract boolean data_getPartial();

    protected abstract byte[] primaryKey_getData();

    protected abstract void cursor_open(Txn txn, IsolationLevel level) throws Exception;

    protected abstract void cursor_close() throws Exception;

    protected abstract boolean cursor_getCurrent() throws Exception;

    protected abstract boolean cursor_getFirst() throws Exception;

    protected abstract boolean cursor_getLast() throws Exception;

    protected abstract boolean cursor_getSearchKeyRange() throws Exception;

    protected abstract boolean cursor_getNext() throws Exception;

    protected abstract boolean cursor_getNextDup() throws Exception;

    protected abstract boolean cursor_getPrev() throws Exception;

    protected abstract boolean cursor_getPrevNoDup() throws Exception;
}