• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
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 android.database.sqlite;
18 
19 import android.content.ContentValues;
20 import android.content.Context;
21 import android.database.AbstractWindowedCursor;
22 import android.database.Cursor;
23 import android.database.CursorWindow;
24 import android.platform.test.annotations.Presubmit;
25 import android.test.AndroidTestCase;
26 
27 import androidx.test.filters.LargeTest;
28 
29 import java.io.File;
30 import java.util.Arrays;
31 import java.util.HashSet;
32 import java.util.Set;
33 
34 @Presubmit
35 @LargeTest
36 public class SQLiteCursorTest extends AndroidTestCase {
37     private static final String TABLE_NAME = "testCursor";
38     private SQLiteDatabase mDatabase;
39     private File mDatabaseFile;
40 
41     @Override
setUp()42     protected void setUp() throws Exception {
43         super.setUp();
44 
45         File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
46         mDatabaseFile = new File(dbDir, "sqlitecursor_test.db");
47         if (mDatabaseFile.exists()) {
48             mDatabaseFile.delete();
49         }
50         mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
51         assertNotNull(mDatabase);
52         // create a test table
53         mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (i int, j int);");
54     }
55 
56     @Override
tearDown()57     protected void tearDown() throws Exception {
58         mDatabase.close();
59         mDatabaseFile.delete();
60         super.tearDown();
61     }
62 
63     /**
64      * this test could take a while to execute. so, designate it as LargeTest
65      */
testFillWindow()66     public void testFillWindow() {
67         // create schema
68         final String testTable = "testV";
69         mDatabase.beginTransaction();
70         mDatabase.execSQL("CREATE TABLE " + testTable + " (col1 int, desc text not null);");
71         mDatabase.setTransactionSuccessful();
72         mDatabase.endTransaction();
73 
74         // populate the table with data
75         // create a big string that will almost fit a page but not quite.
76         // since sqlite wants to make sure each row is in a page, this string will allocate
77         // a new database page for each row.
78         StringBuilder buff = new StringBuilder();
79         for (int i = 0; i < 500; i++) {
80             buff.append(i % 10 + "");
81         }
82         ContentValues values = new ContentValues();
83         values.put("desc", buff.toString());
84 
85         // insert more than 1MB of data in the table. this should ensure that the entire tabledata
86         // will need more than one CursorWindow
87         int N = 5000;
88         Set<Integer> rows = new HashSet<Integer>();
89         mDatabase.beginTransaction();
90         for (int j = 0; j < N; j++) {
91             values.put("col1", j);
92             mDatabase.insert(testTable, null, values);
93             rows.add(j); // store in a hashtable so we can verify the results from cursor later on
94         }
95         mDatabase.setTransactionSuccessful();
96         mDatabase.endTransaction();
97         assertEquals(N, rows.size());
98         Cursor c1 = mDatabase.rawQuery("select * from " + testTable, null);
99         assertEquals(N, c1.getCount());
100         c1.close();
101 
102         // scroll through ALL data in the table using a cursor. should cause multiple calls to
103         // native_fill_window (and re-fills of the CursorWindow object)
104         Cursor c = mDatabase.query(testTable, new String[]{"col1", "desc"},
105                 null, null, null, null, null);
106         int i = 0;
107         while (c.moveToNext()) {
108             int val = c.getInt(0);
109             assertTrue(rows.contains(val));
110             assertTrue(rows.remove(val));
111         }
112         // did I see all the rows in the table?
113         assertTrue(rows.isEmpty());
114 
115         // change data and make sure the cursor picks up new data & count
116         rows = new HashSet<Integer>();
117         mDatabase.beginTransaction();
118         int M = N + 1000;
119         for (int j = 0; j < M; j++) {
120             rows.add(j);
121             if (j < N) {
122                 continue;
123             }
124             values.put("col1", j);
125             mDatabase.insert(testTable, null, values);
126         }
127         mDatabase.setTransactionSuccessful();
128         mDatabase.endTransaction();
129         assertEquals(M, rows.size());
130         c.requery();
131         i = 0;
132         while (c.moveToNext()) {
133             int val = c.getInt(0);
134             assertTrue(rows.contains(val));
135             assertTrue(rows.remove(val));
136         }
137         // did I see all data from the modified table
138         assertTrue(rows.isEmpty());
139 
140         // move cursor back to 1st row and scroll to about halfway in the result set
141         // and then delete 75% of data - and then do requery
142         c.moveToFirst();
143         int K = N / 2;
144         for (int p = 0; p < K && c.moveToNext(); p++) {
145             // nothing to do - just scrolling to about half-point in the resultset
146         }
147         mDatabase.beginTransaction();
148         mDatabase.delete(testTable, "col1 < ?", new String[]{(3 * M / 4) + ""});
149         mDatabase.setTransactionSuccessful();
150         mDatabase.endTransaction();
151         c.requery();
152         assertEquals(M / 4, c.getCount());
153         while (c.moveToNext()) {
154             // just move the cursor to next row - to make sure it can go through the entire
155             // resultset without any problems
156         }
157         c.close();
158     }
159 
testCustomWindowSize()160     public void testCustomWindowSize() {
161         mDatabase.execSQL("CREATE TABLE Tst (Txt BLOB NOT NULL);");
162         byte[] testArr = new byte[10000];
163         Arrays.fill(testArr, (byte) 1);
164         for (int i = 0; i < 10; i++) {
165             mDatabase.execSQL("INSERT INTO Tst VALUES (?)", new Object[]{testArr});
166         }
167         Cursor cursor = mDatabase.rawQuery("SELECT * FROM TST", null);
168         // With default window size, all rows should fit in RAM
169         AbstractWindowedCursor ac = (AbstractWindowedCursor) cursor;
170         int n = 0;
171         while (ac.moveToNext()) {
172             n++;
173             assertEquals(10, ac.getWindow().getNumRows());
174         }
175         assertEquals("All rows should be visited", 10, n);
176 
177         // Now reduce window size, so that only 1 row can fit
178         cursor = mDatabase.rawQuery("SELECT * FROM TST", null);
179         CursorWindow cw = new CursorWindow("test", 11000);
180         ac = (AbstractWindowedCursor) cursor;
181         ac.setWindow(cw);
182         ac.move(-10);
183         n = 0;
184         while (ac.moveToNext()) {
185             n++;
186             assertEquals(1, cw.getNumRows());
187         }
188         assertEquals("All rows should be visited", 10, n);
189     }
190 }