Skip to content

Commit 49dad2e

Browse files
Add native CursorWindow implementation with support dynamic resizing.
Previously the JNI layer mapped query results back into a Java cursor window which was slow. Additionally, the previous implementation allocated an 8 MB backing buffer which was slow for smaller result sets. The new implementation by default will allocate a 16 kb buffer (plus extra for managing the linked list overhead), then grow to 2 MB (plus extra), then double in size as needed. Support for specifying a specific cursor window size is still supported via SQLiteCursor.setCursorWindowSize
1 parent f6aca13 commit 49dad2e

24 files changed

+2637
-470
lines changed

sqlcipher/src/androidTest/java/net/zetetic/database/database_cts/AbstractCursorTest.java

Lines changed: 15 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,21 @@
1717
package net.zetetic.database.database_cts;
1818

1919
import android.content.Context;
20-
import android.database.AbstractCursor;
2120
import android.database.CharArrayBuffer;
2221
import android.database.ContentObserver;
2322
import android.database.CursorIndexOutOfBoundsException;
24-
import android.database.CursorWindow;
2523
import android.database.DataSetObserver;
24+
import net.zetetic.database.AbstractCursor;
25+
import net.zetetic.database.CursorWindow;
2626
import net.zetetic.database.sqlcipher.SQLiteDatabase;
27+
2728
import android.net.Uri;
2829
import android.os.Bundle;
2930
import android.provider.Settings;
3031
import android.test.InstrumentationTestCase;
3132

33+
import androidx.test.filters.Suppress;
34+
3235
import java.io.File;
3336
import java.util.ArrayList;
3437
import java.util.Random;
@@ -169,6 +172,7 @@ public void testOnChange() throws InterruptedException {
169172
assertTrue(mock.hadCalledOnChange());
170173
}
171174

175+
@Suppress
172176
public void testOnMove() {
173177
assertFalse(mTestAbstractCursor.getOnMoveRet());
174178
mTestAbstractCursor.moveToFirst();
@@ -182,6 +186,7 @@ public void testOnMove() {
182186
assertEquals(5, mTestAbstractCursor.getNewPos());
183187
}
184188

189+
@Suppress
185190
public void testOnMove_samePosition() {
186191
mTestAbstractCursor.moveToFirst();
187192
mTestAbstractCursor.moveToPosition(5);
@@ -268,36 +273,10 @@ public void testIsClosed() {
268273
assertTrue(mDatabaseCursor.isClosed());
269274
}
270275

271-
public void testGetWindow() {
272-
CursorWindow window = new CursorWindow(false);
273-
assertEquals(0, window.getNumRows());
274-
// fill window from position 0
275-
mDatabaseCursor.fillWindow(0, window);
276-
277-
assertNotNull(mDatabaseCursor.getWindow());
278-
assertEquals(mDatabaseCursor.getCount(), window.getNumRows());
279-
280-
while (mDatabaseCursor.moveToNext()) {
281-
assertEquals(mDatabaseCursor.getInt(POSITION1),
282-
window.getInt(mDatabaseCursor.getPosition(), POSITION1));
283-
}
284-
window.clear();
285-
}
286-
287276
public void testGetWantsAllOnMoveCalls() {
288277
assertFalse(mDatabaseCursor.getWantsAllOnMoveCalls());
289278
}
290279

291-
public void testIsFieldUpdated() {
292-
mTestAbstractCursor.moveToFirst();
293-
assertFalse(mTestAbstractCursor.isFieldUpdated(0));
294-
}
295-
296-
public void testGetUpdatedField() {
297-
mTestAbstractCursor.moveToFirst();
298-
assertNull(mTestAbstractCursor.getUpdatedField(0));
299-
}
300-
301280
public void testGetExtras() {
302281
assertSame(Bundle.EMPTY, mDatabaseCursor.getExtras());
303282
}
@@ -352,11 +331,12 @@ public void testDeactivate() {
352331
assertTrue(mock.hadCalledOnInvalid());
353332
}
354333

334+
@Suppress
355335
public void testCopyStringToBuffer() {
356336
CharArrayBuffer ca = new CharArrayBuffer(1000);
357337
mTestAbstractCursor.moveToFirst();
358338
mTestAbstractCursor.copyStringToBuffer(0, ca);
359-
CursorWindow window = new CursorWindow(false);
339+
CursorWindow window = new CursorWindow("");
360340
mTestAbstractCursor.fillWindow(0, window);
361341

362342
StringBuffer sb = new StringBuffer();
@@ -366,6 +346,7 @@ public void testCopyStringToBuffer() {
366346
assertEquals(sb.toString(), new String(ca.data, 0, ca.sizeCopied));
367347
}
368348

349+
@Suppress
369350
public void testCheckPosition() {
370351
// Test with position = -1.
371352
try {
@@ -502,7 +483,6 @@ public int getRowsMovedSum() {
502483

503484
@Override
504485
public boolean onMove(int oldPosition, int newPosition) {
505-
mOnMoveReturnValue = super.onMove(oldPosition, newPosition);
506486
mOldPosition = oldPosition;
507487
mNewPosition = newPosition;
508488
mRowsMovedSum += Math.abs(newPosition - oldPosition);
@@ -560,26 +540,20 @@ public boolean isNull(int column) {
560540
return false;
561541
}
562542

543+
@Override
544+
public int getType(int column) {
545+
return 0;
546+
}
547+
563548
public boolean hadCalledOnChange() {
564549
return mHadCalledOnChange;
565550
}
566551

567-
// the following are protected methods
568552
@Override
569553
protected void checkPosition() {
570554
super.checkPosition();
571555
}
572556

573-
@Override
574-
protected Object getUpdatedField(int columnIndex) {
575-
return super.getUpdatedField(columnIndex);
576-
}
577-
578-
@Override
579-
protected boolean isFieldUpdated(int columnIndex) {
580-
return super.isFieldUpdated(columnIndex);
581-
}
582-
583557
@Override
584558
protected void onChange(boolean selfChange) {
585559
super.onChange(selfChange);

sqlcipher/src/androidTest/java/net/zetetic/database/database_cts/CursorWindowTest.java

Lines changed: 20 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
package net.zetetic.database.database_cts;
1818

1919
import android.database.CharArrayBuffer;
20-
import android.database.CursorWindow;
21-
import android.database.MatrixCursor;
2220
import android.database.sqlite.SQLiteException;
23-
import android.os.Parcel;
2421
import android.test.AndroidTestCase;
2522

23+
import net.zetetic.database.CursorWindow;
24+
import net.zetetic.database.MatrixCursor;
25+
26+
import org.junit.Before;
27+
2628
import java.util.ArrayList;
2729
import java.util.Arrays;
2830
import java.util.Random;
@@ -31,6 +33,11 @@ public class CursorWindowTest extends AndroidTestCase {
3133

3234
private static final String TEST_STRING = "Test String";
3335

36+
@Before
37+
public void setUp() {
38+
System.loadLibrary("sqlcipher");
39+
}
40+
3441
public void testWriteCursorToWindow() throws Exception {
3542
// create cursor
3643
String[] colNames = new String[]{"_id", "name", "number", "profit"};
@@ -42,7 +49,7 @@ public void testWriteCursorToWindow() throws Exception {
4249
}
4350

4451
// fill window
45-
CursorWindow window = new CursorWindow(false);
52+
CursorWindow window = new CursorWindow("");
4653
cursor.fillWindow(0, window);
4754

4855
// read from cursor window
@@ -97,34 +104,13 @@ public void testEmptyString() {
97104
}
98105

99106
public void testConstructors() {
100-
int TEST_NUMBER = 5;
101-
CursorWindow cursorWindow;
102-
103107
// Test constructor with 'true' input, and getStartPosition should return 0
104-
cursorWindow = new CursorWindow(true);
105-
assertEquals(0, cursorWindow.getStartPosition());
106-
107-
// Test constructor with 'false' input
108-
cursorWindow = new CursorWindow(false);
108+
CursorWindow cursorWindow = new CursorWindow("");
109109
assertEquals(0, cursorWindow.getStartPosition());
110-
111-
// Test newFromParcel
112-
Parcel parcel = Parcel.obtain();
113-
cursorWindow = new CursorWindow(true);
114-
cursorWindow.setStartPosition(TEST_NUMBER);
115-
cursorWindow.setNumColumns(1);
116-
cursorWindow.allocRow();
117-
cursorWindow.putString(TEST_STRING, TEST_NUMBER, 0);
118-
cursorWindow.writeToParcel(parcel, 0);
119-
120-
parcel.setDataPosition(0);
121-
cursorWindow = CursorWindow.CREATOR.createFromParcel(parcel);
122-
assertEquals(TEST_NUMBER, cursorWindow.getStartPosition());
123-
assertEquals(TEST_STRING, cursorWindow.getString(TEST_NUMBER, 0));
124110
}
125111

126112
public void testDataStructureOperations() {
127-
CursorWindow cursorWindow = new CursorWindow(true);
113+
CursorWindow cursorWindow = new CursorWindow("");
128114

129115
// Test with normal values
130116
assertTrue(cursorWindow.setNumColumns(0));
@@ -145,7 +131,7 @@ public void testDataStructureOperations() {
145131
cursorWindow.freeLastRow();
146132
assertEquals(0, cursorWindow.getNumRows());
147133

148-
cursorWindow = new CursorWindow(true);
134+
cursorWindow = new CursorWindow("");
149135
assertTrue(cursorWindow.setNumColumns(6));
150136
assertTrue(cursorWindow.allocRow());
151137
// Column number set to negative number, so now can put values.
@@ -190,7 +176,7 @@ public void testAccessDataValues() {
190176
originalBlob[i] = (byte) i;
191177
}
192178

193-
CursorWindow cursorWindow = new CursorWindow(true);
179+
CursorWindow cursorWindow = new CursorWindow("");
194180
cursorWindow.setNumColumns(5);
195181
cursorWindow.allocRow();
196182

@@ -280,15 +266,15 @@ public void testAccessDataValues() {
280266
}
281267

282268
public void testCopyStringToBuffer() {
283-
int DEFAULT_ARRAY_LENGTH = 64;
269+
int DEFAULT_ARRAY_LENGTH = 60;
284270
String baseString = "0123456789";
285271
String expectedString = "";
286272
// Create a 60 characters string.
287273
for (int i = 0; i < 6; i++) {
288274
expectedString += baseString;
289275
}
290276
CharArrayBuffer charArrayBuffer = new CharArrayBuffer(null);
291-
CursorWindow cursorWindow = new CursorWindow(true);
277+
CursorWindow cursorWindow = new CursorWindow("");
292278
cursorWindow.setNumColumns(2);
293279
cursorWindow.allocRow();
294280

@@ -318,7 +304,7 @@ public void testAccessStartPosition() {
318304
final int TEST_POSITION_1 = 0;
319305
final int TEST_POSITION_2 = 3;
320306

321-
CursorWindow cursorWindow = new CursorWindow(true);
307+
CursorWindow cursorWindow = new CursorWindow("");
322308
fillCursorTestContents(cursorWindow, 5);
323309

324310
// Test setStartPosition
@@ -369,16 +355,11 @@ public void testClearAndOnAllReferencesReleased() {
369355
assertTrue(cursorWindow.hasReleasedAllReferences());
370356
}
371357

372-
public void testDescribeContents() {
373-
CursorWindow cursorWindow = new CursorWindow(true);
374-
assertEquals(0, cursorWindow.describeContents());
375-
}
376-
377358
private class MockCursorWindow extends CursorWindow {
378359
private boolean mHasReleasedAllReferences = false;
379360

380361
public MockCursorWindow(boolean localWindow) {
381-
super(localWindow);
362+
super("");
382363
}
383364

384365
@Override
@@ -426,7 +407,7 @@ private static ArrayList<ArrayList<Integer>> createTestList(int rows, int cols)
426407
* The method comes from unit_test CursorWindowTest.
427408
*/
428409
private CursorWindow getOneByOneWindow() {
429-
CursorWindow window = new CursorWindow(false);
410+
CursorWindow window = new CursorWindow("");
430411
assertTrue(window.setNumColumns(1));
431412
assertTrue(window.allocRow());
432413
return window;

sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/RoomUpsertTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public static abstract class UserDao {
7272
abstract User[] findById(long id);
7373
}
7474

75-
@Database(entities = {User.class}, version = 1)
75+
@Database(entities = {User.class}, version = 1, exportSchema = false)
7676
public static abstract class UserDatabase extends RoomDatabase {
7777
abstract UserDao userDao();
7878
}

0 commit comments

Comments
 (0)